agent window: a site's relay stands in for the endpoint it relays
Deploy web Studio / deploy (push) Successful in 3m53s
Deploy web Studio / deploy (push) Successful in 3m53s
On isokit.buildfor.life, entering https://inference.p0.contact/v1 in the agent window's settings made the page call the inference server directly, which browsers refuse (its nginx answers the CORS preflight with 401): "The browser could not reach ... (Failed to fetch)". - the relay meta names its upstream (data-upstream; build_web.py --agent-upstream, the deploy passes the inference server): that URL now goes through the relay and is saved as "follow the site's default" - an unreachable endpoint says what to do: the relay's URL on a site with one, else isokit studio or isokit.buildfor.life - check_web.py: relayed upstream and the unreachable message
This commit is contained in:
1 parent
1a1b62d475
commit
41554801ea
4 files changed
+65
-11
No files matched your search
+30
-7
@@ -6,8 +6,11 @@
|
||||
* Under `isokit studio` the model is reached through the Studio server
|
||||
* (/api/agent/*, crates/isokit-cli/src/agent.rs): the key stays on the machine
|
||||
* and the endpoint needs no CORS. The web Studio calls the endpoint from the
|
||||
* page with a key kept in this browser; a build can name a same-site relay as
|
||||
* the default endpoint (<meta name="isokit-agent" content="/llm/v1">).
|
||||
* page with a key kept in this browser. Most inference servers refuse calls
|
||||
* from web pages on other sites (CORS), so a build can name a same-site relay
|
||||
* (<meta name="isokit-agent" content="/llm/v1" data-upstream="https://…/v1">):
|
||||
* it is the default endpoint, and the upstream URL, when entered, goes
|
||||
* through it too.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
@@ -29,7 +32,15 @@
|
||||
function saveUi() { lsSet(LS_UI, A.ui); }
|
||||
|
||||
// ------------------------------------------------------------ settings
|
||||
function siteDefault() { var m = document.querySelector('meta[name="isokit-agent"]'); return (m && m.getAttribute("content")) || DEFAULT_URL; }
|
||||
function norm(u) { return String(u || "").trim().replace(/\/+$/, ""); }
|
||||
/** This site's relay to an endpoint elsewhere (build_web.py --agent-url), or null. */
|
||||
function relay() {
|
||||
var m = document.querySelector('meta[name="isokit-agent"]');
|
||||
return m && m.getAttribute("content") ? { url: norm(m.getAttribute("content")), upstream: norm(m.getAttribute("data-upstream") || DEFAULT_URL) } : null;
|
||||
}
|
||||
function siteDefault() { var r = relay(); return r ? r.url : DEFAULT_URL; }
|
||||
/** Same as the site's default (the relay, or the endpoint it relays)? */
|
||||
function isDefault(u) { var r = relay(); u = norm(u); return u === norm(siteDefault()) || (!!r && u === r.upstream); }
|
||||
function loadCfg() {
|
||||
if (!web()) return fetch("/api/agent/config", { cache: "no-store" }).then(json).then(function (c) { A.cfg = c; return c; });
|
||||
var c = lsGet(LS_CFG) || {};
|
||||
@@ -44,12 +55,24 @@
|
||||
}
|
||||
var c = lsGet(LS_CFG) || {};
|
||||
["url", "model", "key", "think"].forEach(function (k) { if (v[k] !== undefined) c[k] = typeof v[k] === "string" ? v[k].trim() : v[k]; });
|
||||
if (c.url === siteDefault()) delete c.url; // follow the site's default when it moves
|
||||
if (c.url !== undefined && isDefault(c.url)) delete c.url; // follow the site's default when it moves
|
||||
lsSet(LS_CFG, c);
|
||||
return loadCfg();
|
||||
}
|
||||
function baseUrl() { return new URL(A.cfg.url.replace(/\/+$/, ""), location.href).href; }
|
||||
/** The URL the page calls: the relay stands in for the endpoint it relays. */
|
||||
function baseUrl() {
|
||||
var r = relay(), u = norm(A.cfg.url);
|
||||
if (r && u === r.upstream) u = r.url;
|
||||
return new URL(u, location.href).href;
|
||||
}
|
||||
function host() { try { var u = new URL(baseUrl()); return u.host === location.host ? "this site's relay" : u.host; } catch (e) { return A.cfg.url; } }
|
||||
function unreachable(e) {
|
||||
var u = baseUrl(), r = relay(), why = " (" + (e && e.message || e) + ").";
|
||||
if (new URL(u).origin === location.origin) return "The browser could not reach this site's relay " + u + why + " The relay or the inference server behind it is down.";
|
||||
return "The browser could not reach " + u + why + " Web pages may only call a server on another site when it allows them (CORS, including the preflight request, which carries no key); most inference servers do not. " +
|
||||
(r ? "Set the endpoint to " + r.url + ": this site relays that to " + r.upstream + "."
|
||||
: "Use the agent in `isokit studio` (it passes the calls on itself) or on https://isokit.buildfor.life (it relays to B4L's inference server).");
|
||||
}
|
||||
function model() { return A.cfg.model || A.models[0] || ""; }
|
||||
|
||||
// ------------------------------------------------------------ the model's endpoint
|
||||
@@ -60,7 +83,7 @@
|
||||
if (A.cfg.key) o.headers.Authorization = "Bearer " + A.cfg.key;
|
||||
return fetch(baseUrl() + path, o).catch(function (e) {
|
||||
if (e && e.name === "AbortError") throw e;
|
||||
throw new Error("The browser could not reach " + baseUrl() + " (" + (e && e.message || e) + "). An endpoint on another site has to allow this page (CORS, including the preflight request, which carries no key), or use a relay on this site.");
|
||||
throw new Error(unreachable(e));
|
||||
});
|
||||
}
|
||||
function httpError(r) {
|
||||
@@ -465,7 +488,7 @@
|
||||
names.map(function (m) { return "<option" + (m === c.model ? " selected" : "") + ">" + esc(m) + "</option>"; }).join("");
|
||||
$("agthink").checked = c.think !== false;
|
||||
$("agsetnote").innerHTML = web()
|
||||
? "Kept in this browser only." + (siteDefault() !== DEFAULT_URL ? " Default endpoint: " + esc(siteDefault()) + " (this site's relay to B4L's inference server)." : "")
|
||||
? "Kept in this browser only." + (relay() ? " " + esc(relay().url) + " is this site's relay to " + esc(relay().upstream) + " (entering that URL uses the relay too)." : "")
|
||||
: "Kept by the Studio server" + (c.file ? " in <code>" + esc(c.file) + "</code>" : "") + "; the key never reaches this page.";
|
||||
setMsg(A.modelsErr ? A.modelsErr : A.models.length ? "Connected: " + A.models.length + " model" + (A.models.length === 1 ? "" : "s") : "", !!A.modelsErr);
|
||||
f.hidden = false;
|
||||
|
||||
Reference in new issue
Block a user