Add inline credential validation endpoints and UI for CivitAI, HuggingFace, and Brave Search
This commit is contained in:
+58
@@ -101,6 +101,8 @@ class ModelLinkerExtension:
|
||||
search_huggingface_for_file,
|
||||
get_author_fallback_index_status,
|
||||
refresh_author_fallback_index,
|
||||
check_huggingface_token,
|
||||
check_brave_search_api_key,
|
||||
clear_search_cache as clear_huggingface_search_cache,
|
||||
)
|
||||
from .core.sources.civitai import (
|
||||
@@ -108,6 +110,8 @@ class ModelLinkerExtension:
|
||||
search_civitai,
|
||||
get_civitai_download_url,
|
||||
resolve_urn,
|
||||
check_civitai_api_key,
|
||||
check_civitai_session_token,
|
||||
clear_search_cache as clear_civitai_search_cache,
|
||||
)
|
||||
from .core.sources.civarchive import (
|
||||
@@ -1270,6 +1274,60 @@ class ModelLinkerExtension:
|
||||
log_exception(f"Clear search cache error: {e}")
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
@routes.post("/model_linker/civitai/session-token/check")
|
||||
async def civitai_session_token_check_route(request):
|
||||
"""Check whether a CivitAI browser session token is valid."""
|
||||
try:
|
||||
data = await request.json()
|
||||
token = data.get("civitai_session_token", "")
|
||||
result = await asyncio.to_thread(
|
||||
check_civitai_session_token, token
|
||||
)
|
||||
return web.json_response(result)
|
||||
except Exception as e:
|
||||
log_exception(f"CivitAI session token check error: {e}")
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
@routes.post("/model_linker/civitai/api-key/check")
|
||||
async def civitai_api_key_check_route(request):
|
||||
"""Check whether a CivitAI API key is valid."""
|
||||
try:
|
||||
data = await request.json()
|
||||
api_key = data.get("civitai_key", "")
|
||||
result = await asyncio.to_thread(
|
||||
check_civitai_api_key, api_key
|
||||
)
|
||||
return web.json_response(result)
|
||||
except Exception as e:
|
||||
log_exception(f"CivitAI API key check error: {e}")
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
@routes.post("/model_linker/huggingface/token/check")
|
||||
async def huggingface_token_check_route(request):
|
||||
"""Check whether a HuggingFace token is valid."""
|
||||
try:
|
||||
data = await request.json()
|
||||
token = data.get("hf_token", "")
|
||||
result = await asyncio.to_thread(check_huggingface_token, token)
|
||||
return web.json_response(result)
|
||||
except Exception as e:
|
||||
log_exception(f"HuggingFace token check error: {e}")
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
@routes.post("/model_linker/brave/api-key/check")
|
||||
async def brave_api_key_check_route(request):
|
||||
"""Check whether a Brave Search API key is valid."""
|
||||
try:
|
||||
data = await request.json()
|
||||
api_key = data.get("brave_search_api_key", "")
|
||||
result = await asyncio.to_thread(
|
||||
check_brave_search_api_key, api_key
|
||||
)
|
||||
return web.json_response(result)
|
||||
except Exception as e:
|
||||
log_exception(f"Brave Search API key check error: {e}")
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
@routes.get("/model_linker/huggingface/author-index/status")
|
||||
async def huggingface_author_index_status_route(request):
|
||||
"""Return local HuggingFace author fallback index status."""
|
||||
|
||||
@@ -50,6 +50,170 @@ def clear_search_cache():
|
||||
_search_cache.clear()
|
||||
|
||||
|
||||
def check_civitai_session_token(session_token: Optional[str]) -> Dict[str, Any]:
|
||||
"""Check whether a CivitAI browser session token is accepted by civitai.com."""
|
||||
token = (session_token or "").strip()
|
||||
if not token:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "missing",
|
||||
"message": "Paste a CivitAI session token first.",
|
||||
}
|
||||
|
||||
headers = {
|
||||
"accept": "application/json",
|
||||
"Cookie": f"__Secure-civitai-token={token}",
|
||||
"user-agent": (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/147.0.0.0 Safari/537.36"
|
||||
),
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
"https://civitai.com/api/v1/me", headers=headers, timeout=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
username = ""
|
||||
try:
|
||||
data = response.json()
|
||||
username = (
|
||||
data.get("username")
|
||||
or data.get("name")
|
||||
or data.get("email")
|
||||
or ""
|
||||
)
|
||||
except Exception:
|
||||
username = ""
|
||||
|
||||
message = "Session token is valid."
|
||||
if username:
|
||||
message = f"Session token is valid for {username}."
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"valid": True,
|
||||
"status": "valid",
|
||||
"message": message,
|
||||
"username": username,
|
||||
}
|
||||
|
||||
if response.status_code in {401, 403}:
|
||||
return {
|
||||
"success": True,
|
||||
"valid": False,
|
||||
"status": "invalid",
|
||||
"message": "Session token is not accepted by CivitAI.",
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "error",
|
||||
"message": f"CivitAI returned HTTP {response.status_code}.",
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
except requests.exceptions.Timeout:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "timeout",
|
||||
"message": "CivitAI did not respond before the timeout.",
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "error",
|
||||
"message": str(e),
|
||||
}
|
||||
|
||||
|
||||
def check_civitai_api_key(api_key: Optional[str]) -> Dict[str, Any]:
|
||||
"""Check whether a CivitAI API key is accepted by civitai.com."""
|
||||
key = (api_key or "").strip()
|
||||
if not key:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "missing",
|
||||
"message": "Paste a CivitAI API key first.",
|
||||
}
|
||||
|
||||
headers = {
|
||||
"accept": "application/json",
|
||||
"Authorization": f"Bearer {key}",
|
||||
"user-agent": (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/147.0.0.0 Safari/537.36"
|
||||
),
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
"https://civitai.com/api/v1/me", headers=headers, timeout=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
username = ""
|
||||
try:
|
||||
data = response.json()
|
||||
username = (
|
||||
data.get("username")
|
||||
or data.get("name")
|
||||
or data.get("email")
|
||||
or ""
|
||||
)
|
||||
except Exception:
|
||||
username = ""
|
||||
|
||||
message = "CivitAI API key is valid."
|
||||
if username:
|
||||
message = f"CivitAI API key is valid for {username}."
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"valid": True,
|
||||
"status": "valid",
|
||||
"message": message,
|
||||
"username": username,
|
||||
}
|
||||
|
||||
if response.status_code in {401, 403}:
|
||||
return {
|
||||
"success": True,
|
||||
"valid": False,
|
||||
"status": "invalid",
|
||||
"message": "CivitAI API key is not accepted.",
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "error",
|
||||
"message": f"CivitAI returned HTTP {response.status_code}.",
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
except requests.exceptions.Timeout:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "timeout",
|
||||
"message": "CivitAI did not respond before the timeout.",
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "error",
|
||||
"message": str(e),
|
||||
}
|
||||
|
||||
|
||||
def _build_civitai_result_from_version(
|
||||
model_id: int,
|
||||
model_name: str,
|
||||
|
||||
@@ -45,6 +45,141 @@ def clear_search_cache():
|
||||
_search_cache.clear()
|
||||
|
||||
|
||||
def check_huggingface_token(token: Optional[str]) -> Dict[str, Any]:
|
||||
"""Check whether a HuggingFace access token is accepted."""
|
||||
value = (token or "").strip()
|
||||
if not value:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "missing",
|
||||
"message": "Paste a HuggingFace token first.",
|
||||
}
|
||||
|
||||
headers = {"Authorization": f"Bearer {value}"}
|
||||
try:
|
||||
response = requests.get(f"{HF_API_URL}/whoami-v2", headers=headers, timeout=10)
|
||||
if response.status_code == 404:
|
||||
response = requests.get(f"{HF_API_URL}/whoami", headers=headers, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
username = ""
|
||||
try:
|
||||
data = response.json()
|
||||
username = data.get("name") or data.get("user", {}).get("name") or ""
|
||||
except Exception:
|
||||
username = ""
|
||||
|
||||
message = "HuggingFace token is valid."
|
||||
if username:
|
||||
message = f"HuggingFace token is valid for {username}."
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"valid": True,
|
||||
"status": "valid",
|
||||
"message": message,
|
||||
"username": username,
|
||||
}
|
||||
|
||||
if response.status_code in {401, 403}:
|
||||
return {
|
||||
"success": True,
|
||||
"valid": False,
|
||||
"status": "invalid",
|
||||
"message": "HuggingFace token is not accepted.",
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "error",
|
||||
"message": f"HuggingFace returned HTTP {response.status_code}.",
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
except requests.exceptions.Timeout:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "timeout",
|
||||
"message": "HuggingFace did not respond before the timeout.",
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "error",
|
||||
"message": str(e),
|
||||
}
|
||||
|
||||
|
||||
def check_brave_search_api_key(api_key: Optional[str]) -> Dict[str, Any]:
|
||||
"""Check whether a Brave Search API key is accepted."""
|
||||
key = (api_key or "").strip()
|
||||
if not key:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "missing",
|
||||
"message": "Paste a Brave Search API key first.",
|
||||
}
|
||||
|
||||
headers = {"X-Subscription-Token": key}
|
||||
params = {"q": "test", "count": 1}
|
||||
try:
|
||||
response = requests.get(
|
||||
BRAVE_SEARCH_API_URL, headers=headers, params=params, timeout=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
return {
|
||||
"success": True,
|
||||
"valid": True,
|
||||
"status": "valid",
|
||||
"message": "Brave Search API key is valid.",
|
||||
}
|
||||
|
||||
if response.status_code in {401, 403}:
|
||||
return {
|
||||
"success": True,
|
||||
"valid": False,
|
||||
"status": "invalid",
|
||||
"message": "Brave Search API key is not accepted.",
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
|
||||
if response.status_code == 429:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "limited",
|
||||
"message": "Brave Search rate limit was reached.",
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "error",
|
||||
"message": f"Brave Search returned HTTP {response.status_code}.",
|
||||
"status_code": response.status_code,
|
||||
}
|
||||
except requests.exceptions.Timeout:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "timeout",
|
||||
"message": "Brave Search did not respond before the timeout.",
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"valid": False,
|
||||
"status": "error",
|
||||
"message": str(e),
|
||||
}
|
||||
|
||||
|
||||
def _author_index_cache_key(author: str, headers: Dict[str, str]) -> str:
|
||||
auth_key = "token" if headers.get("Authorization") else "public"
|
||||
return f"{author}::{auth_key}"
|
||||
|
||||
+29
-2
@@ -3542,6 +3542,8 @@
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
align-items: stretch;
|
||||
padding-bottom: 14px;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
.ml-options-help,
|
||||
#model-linker-modal .ml-options-help {
|
||||
@@ -3579,6 +3581,32 @@
|
||||
color: var(--ml-text);
|
||||
font-size: var(--ml-font-xs);
|
||||
}
|
||||
.ml-options-token-check,
|
||||
#model-linker-modal .ml-options-token-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.ml-options-token-check-status,
|
||||
#model-linker-modal .ml-options-token-check-status {
|
||||
color: var(--ml-text-muted);
|
||||
font-size: var(--ml-font-sm);
|
||||
font-weight: 600;
|
||||
}
|
||||
.ml-options-token-check-status.is-pending,
|
||||
#model-linker-modal .ml-options-token-check-status.is-pending {
|
||||
color: var(--ml-text-secondary);
|
||||
}
|
||||
.ml-options-token-check-status.is-valid,
|
||||
#model-linker-modal .ml-options-token-check-status.is-valid {
|
||||
color: var(--ml-color-success);
|
||||
}
|
||||
.ml-options-token-check-status.is-invalid,
|
||||
#model-linker-modal .ml-options-token-check-status.is-invalid {
|
||||
color: var(--ml-color-error-light);
|
||||
}
|
||||
.ml-options-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -3650,8 +3678,7 @@
|
||||
grid-template-columns: minmax(150px, 1fr) minmax(0, 380px) 42px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 14px 0;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
padding: 14px 0 0;
|
||||
min-width: 0;
|
||||
}
|
||||
.ml-options-input-row > .ml-options-label {
|
||||
|
||||
+115
@@ -4051,6 +4051,10 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
${getSvgIcon('eye')}
|
||||
</button>
|
||||
</div>
|
||||
<div class="ml-options-token-check">
|
||||
<button id="ml-options-civitai-key-check" type="button" class="ml-btn ml-btn-secondary ml-btn-sm">${getSvgIcon('refreshCw')} Check API key</button>
|
||||
<span id="ml-options-civitai-key-check-status" class="ml-options-token-check-status">Not checked</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-options-field">
|
||||
<div class="ml-options-input-row">
|
||||
@@ -4069,6 +4073,10 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
</ol>
|
||||
<p>Treat it like a password. It is only needed when session-aware CivitAI search is required.</p>
|
||||
</details>
|
||||
<div class="ml-options-token-check">
|
||||
<button id="ml-options-civitai-session-check" type="button" class="ml-btn ml-btn-secondary ml-btn-sm">${getSvgIcon('refreshCw')} Check session</button>
|
||||
<span id="ml-options-civitai-session-check-status" class="ml-options-token-check-status">Not checked</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="ml-options-toggle-row">
|
||||
@@ -4117,6 +4125,10 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
${getSvgIcon('eye')}
|
||||
</button>
|
||||
</div>
|
||||
<div class="ml-options-token-check">
|
||||
<button id="ml-options-hf-token-check" type="button" class="ml-btn ml-btn-secondary ml-btn-sm">${getSvgIcon('refreshCw')} Check token</button>
|
||||
<span id="ml-options-hf-token-check-status" class="ml-options-token-check-status">Not checked</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="ml-options-toggle-row">
|
||||
@@ -4166,6 +4178,10 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
${getSvgIcon('eye')}
|
||||
</button>
|
||||
</div>
|
||||
<div class="ml-options-token-check">
|
||||
<button id="ml-options-brave-key-check" type="button" class="ml-btn ml-btn-secondary ml-btn-sm">${getSvgIcon('refreshCw')} Check API key</button>
|
||||
<span id="ml-options-brave-key-check-status" class="ml-options-token-check-status">Not checked</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4188,9 +4204,17 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
const civitaiLimitInput = this.contentElement.querySelector('#ml-options-civitai-limit');
|
||||
const civitaiUseTrpcSearchInput = this.contentElement.querySelector('#ml-options-civitai-use-trpc-search');
|
||||
const civitaiUseHtmlFallbackInput = this.contentElement.querySelector('#ml-options-civitai-use-html-fallback');
|
||||
const civitaiKeyCheckBtn = this.contentElement.querySelector('#ml-options-civitai-key-check');
|
||||
const civitaiKeyCheckStatus = this.contentElement.querySelector('#ml-options-civitai-key-check-status');
|
||||
const civitaiSessionCheckBtn = this.contentElement.querySelector('#ml-options-civitai-session-check');
|
||||
const civitaiSessionCheckStatus = this.contentElement.querySelector('#ml-options-civitai-session-check-status');
|
||||
const hfUseApiSearchInput = this.contentElement.querySelector('#ml-options-hf-use-api-search');
|
||||
const hfUseComfyOrgFallbackInput = this.contentElement.querySelector('#ml-options-hf-use-comfy-org-fallback');
|
||||
const hfUseBraveFallbackInput = this.contentElement.querySelector('#ml-options-hf-use-brave-fallback');
|
||||
const hfTokenCheckBtn = this.contentElement.querySelector('#ml-options-hf-token-check');
|
||||
const hfTokenCheckStatus = this.contentElement.querySelector('#ml-options-hf-token-check-status');
|
||||
const braveKeyCheckBtn = this.contentElement.querySelector('#ml-options-brave-key-check');
|
||||
const braveKeyCheckStatus = this.contentElement.querySelector('#ml-options-brave-key-check-status');
|
||||
const sourceEnabledInputs = Array.from(this.contentElement.querySelectorAll('.ml-options-source-enabled'));
|
||||
const status = this.contentElement.querySelector('#ml-options-status');
|
||||
const saveBtn = this.contentElement.querySelector('#ml-options-save');
|
||||
@@ -4246,6 +4270,45 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
if (hfIndexRefreshBtn) hfIndexRefreshBtn.disabled = busy;
|
||||
};
|
||||
|
||||
const setTokenCheckStatus = (statusEl, text, mode = '') => {
|
||||
if (!statusEl) return;
|
||||
statusEl.textContent = text;
|
||||
statusEl.classList.remove('is-valid', 'is-invalid', 'is-pending');
|
||||
if (mode) statusEl.classList.add(mode);
|
||||
};
|
||||
|
||||
const checkCredential = async ({ input, button, statusEl, endpoint, payloadKey, missingText }) => {
|
||||
const value = (input?.value || '').trim();
|
||||
if (!value) {
|
||||
setTokenCheckStatus(statusEl, missingText, 'is-invalid');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (button) button.disabled = true;
|
||||
setTokenCheckStatus(statusEl, 'Checking...', 'is-pending');
|
||||
const response = await api.fetchApi(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ [payloadKey]: value })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || `Check failed: ${response.status}`);
|
||||
}
|
||||
setTokenCheckStatus(
|
||||
statusEl,
|
||||
data.message || (data.valid ? 'Valid' : 'Invalid'),
|
||||
data.valid ? 'is-valid' : 'is-invalid'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Model Linker: Credential check error:', error);
|
||||
setTokenCheckStatus(statusEl, error.message || 'Check failed', 'is-invalid');
|
||||
} finally {
|
||||
if (button) button.disabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
const renderModelListStatus = (data = {}, checkedRemote = false) => {
|
||||
if (modelListCountEl) {
|
||||
modelListCountEl.textContent = Number.isFinite(Number(data.local_count))
|
||||
@@ -4494,6 +4557,58 @@ class LinkerManagerDialog extends ComfyDialog {
|
||||
});
|
||||
}
|
||||
|
||||
if (civitaiSessionCheckBtn) {
|
||||
civitaiSessionCheckBtn.addEventListener('click', () => {
|
||||
checkCredential({
|
||||
input: civitaiSessionInput,
|
||||
button: civitaiSessionCheckBtn,
|
||||
statusEl: civitaiSessionCheckStatus,
|
||||
endpoint: '/model_linker/civitai/session-token/check',
|
||||
payloadKey: 'civitai_session_token',
|
||||
missingText: 'Paste token first'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (civitaiKeyCheckBtn) {
|
||||
civitaiKeyCheckBtn.addEventListener('click', () => {
|
||||
checkCredential({
|
||||
input: civitaiInput,
|
||||
button: civitaiKeyCheckBtn,
|
||||
statusEl: civitaiKeyCheckStatus,
|
||||
endpoint: '/model_linker/civitai/api-key/check',
|
||||
payloadKey: 'civitai_key',
|
||||
missingText: 'Paste API key first'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (hfTokenCheckBtn) {
|
||||
hfTokenCheckBtn.addEventListener('click', () => {
|
||||
checkCredential({
|
||||
input: hfInput,
|
||||
button: hfTokenCheckBtn,
|
||||
statusEl: hfTokenCheckStatus,
|
||||
endpoint: '/model_linker/huggingface/token/check',
|
||||
payloadKey: 'hf_token',
|
||||
missingText: 'Paste token first'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (braveKeyCheckBtn) {
|
||||
braveKeyCheckBtn.addEventListener('click', () => {
|
||||
checkCredential({
|
||||
input: braveInput,
|
||||
button: braveKeyCheckBtn,
|
||||
statusEl: braveKeyCheckStatus,
|
||||
endpoint: '/model_linker/brave/api-key/check',
|
||||
payloadKey: 'brave_search_api_key',
|
||||
missingText: 'Paste API key first'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
loadModelListStatus(false);
|
||||
loadHfIndexStatus();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user