Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6ed974ece |
Generated
+1
-1
@@ -285,7 +285,7 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "cortiq-gateway"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cortiq-gateway"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
edition = "2021"
|
||||
rust-version = "1.75"
|
||||
license = "Apache-2.0"
|
||||
|
||||
+15
-1
@@ -749,7 +749,21 @@ async fn probe_model(State(state): State<SharedState>, Path(id): Path<String>) -
|
||||
let latency_ms = started.elapsed().as_millis() as u64;
|
||||
match res {
|
||||
Ok(_) => ok(json!({ "ok": true, "latency_ms": latency_ms })),
|
||||
Err(e) => ok(json!({ "ok": false, "latency_ms": latency_ms, "error": e.to_string() })),
|
||||
Err(e) => {
|
||||
let msg = e.to_string();
|
||||
// Make auth failures actionable — a raw "User not found" (OpenRouter's
|
||||
// 401) usually means the API key is wrong or the wrong type.
|
||||
let hint = if msg.contains("401")
|
||||
|| msg.contains("Unauthorized")
|
||||
|| msg.contains("User not found")
|
||||
|| msg.contains("No auth")
|
||||
{
|
||||
" — check the API key (for OpenRouter use an inference key, not a provisioning/management key)"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
ok(json!({ "ok": false, "latency_ms": latency_ms, "error": format!("{msg}{hint}") }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
@@ -129,6 +129,7 @@ export default {
|
||||
"models.form.secretMissing": "not set",
|
||||
"models.form.secretPlaceholder": "set or replace key (write-only)",
|
||||
"models.deleteConfirm": "Delete model \"{id}\"?",
|
||||
"models.form.idOrModel": "Enter an id or a model name",
|
||||
"models.form.apiKey": "API Key",
|
||||
"models.form.apiKeyPlaceholder": "paste your API key",
|
||||
"models.form.apiKeyNotNeeded": "not required (local server)",
|
||||
|
||||
Vendored
+1
@@ -128,6 +128,7 @@ export default {
|
||||
"models.form.secretMissing": "не задан",
|
||||
"models.form.secretPlaceholder": "задать или заменить ключ (только запись)",
|
||||
"models.deleteConfirm": "Удалить модель \"{id}\"?",
|
||||
"models.form.idOrModel": "Укажите id или имя модели",
|
||||
"models.form.apiKey": "API-ключ",
|
||||
"models.form.apiKeyPlaceholder": "вставьте API-ключ",
|
||||
"models.form.apiKeyNotNeeded": "не нужен (локальный сервер)",
|
||||
|
||||
Vendored
+22
-1
@@ -26,6 +26,13 @@ const PROVIDER_DEFAULTS = {
|
||||
http: { base: "", keyEnv: "", needsKey: false, tier: "cheap", caps: ["tools"] },
|
||||
};
|
||||
|
||||
// Derive a clean model id from a model name (last path segment, slugified).
|
||||
// e.g. "qwen/qwen3.7-max" → "qwen3-7-max".
|
||||
function slugId(name) {
|
||||
const base = (name || "").split("/").pop() || "";
|
||||
return base.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
||||
}
|
||||
|
||||
function modelForm(meta, existing) {
|
||||
const m = existing || { provider: "openai", kind: "chat", cost_tier: "mid", caps: ["tools"], price_in: 0, price_out: 0 };
|
||||
const idIn = h("input", { value: m.id || "", placeholder: "my-model", disabled: existing ? true : null });
|
||||
@@ -70,6 +77,17 @@ function modelForm(meta, existing) {
|
||||
providerSel.addEventListener("change", applyDefaults);
|
||||
if (!existing) applyDefaults();
|
||||
|
||||
// Auto-fill the id from the model name (until the user edits id by hand).
|
||||
if (!existing) {
|
||||
modelIn.addEventListener("input", () => {
|
||||
if (!idIn.value || idIn._auto) {
|
||||
idIn.value = slugId(modelIn.value);
|
||||
idIn._auto = true;
|
||||
}
|
||||
});
|
||||
idIn.addEventListener("input", () => { idIn._auto = false; });
|
||||
}
|
||||
|
||||
async function testKey() {
|
||||
if (!baseUrlIn.value.trim()) { mount(keyStatus, h("span", { style: "color:var(--bad)" }, t("models.form.baseUrl") + "?")); return; }
|
||||
testBtn.disabled = true;
|
||||
@@ -142,8 +160,11 @@ async function openModal(meta, existing, reload) {
|
||||
const { node, getValue } = modelForm(meta, existing);
|
||||
modal(existing ? t("models.form.editTitle") : t("models.form.addTitle"), node, async () => {
|
||||
const { model, secret, keyEnv } = getValue();
|
||||
// Forgot the id? Derive it from the model name so saving never errors on a
|
||||
// blank id.
|
||||
if (!model.id) model.id = slugId(model.model);
|
||||
if (!model.id) {
|
||||
toast(t("models.form.id") + "?", "bad");
|
||||
toast(t("models.form.idOrModel"), "bad");
|
||||
return false;
|
||||
}
|
||||
if (!model.api_key_env) delete model.api_key_env; // keep serde field optional
|
||||
|
||||
Reference in New Issue
Block a user