ruslanmv's picture
Deploy Matrix Context Console
ce45eb0 verified
Raw
History Blame Contribute Delete
5.38 kB
/* Matrix Context — live API adapter (Phase 0, Compatible Mode).
*
* Drop-in replacement for the design's mock `window.MC`: same method names,
* real `fetch` against the same-origin `/v1` surface. This is the ONE place that
* maps the backend response shapes to what the UI consumes:
* - remember -> read res.item (backend returns {item:{…}})
* - inspect -> res.routing.* + res.pack.* (selected/unselected/scores/…)
* - version -> contract_version / implementation_version
* - scopes -> plain strings
* No contract changes; works against MoC Contract v1 as-is.
*/
(function () {
const BASE = location.origin + "/v1";
// Optional bearer token (Phase 1 servers may require it; harmless if unset).
let TOKEN = (window.localStorage && localStorage.getItem("mc_token")) || "";
function setToken(t) {
TOKEN = t || "";
if (window.localStorage) localStorage.setItem("mc_token", TOKEN);
}
async function call(method, path, body) {
const headers = { "Content-Type": "application/json" };
if (TOKEN) headers["Authorization"] = "Bearer " + TOKEN;
const opt = { method, headers };
if (body !== undefined) opt.body = JSON.stringify(body);
let resp;
try {
resp = await fetch(BASE + path, opt);
} catch (e) {
throw new ApiError(0, "network error — is the backend running?");
}
let data = {};
try { data = await resp.json(); } catch (e) { /* empty body */ }
if (!resp.ok) throw new ApiError(resp.status, (data && data.error) || ("HTTP " + resp.status));
return data;
}
function ApiError(status, message) { this.status = status; this.message = message; }
ApiError.prototype = Object.create(Error.prototype);
const MC = {
base: BASE,
setToken,
hasToken: () => !!TOKEN,
ApiError,
// ---- discovery ----
async health() {
const h = await call("GET", "/health");
return { status: h.status, name: h.name, version: h.version, items: h.items };
},
async version() {
const v = await call("GET", "/version");
return {
contract: v.contract_version,
implementation: v.implementation,
build: v.implementation_version,
name: v.name,
};
},
async experts() {
const r = await call("GET", "/experts");
return (r.experts || []).map((e) => ({ id: e.name, name: e.name, desc: e.description || "" }));
},
async scopes() {
const r = await call("GET", "/scopes");
return (r.scopes || []).map((s) => ({ id: s, label: s })); // plain strings -> objects
},
// ---- items ----
async items(filter) {
filter = filter || {};
const q = [];
if (filter.scope) q.push("scope=" + encodeURIComponent(filter.scope));
if (filter.expert) q.push("expert=" + encodeURIComponent(filter.expert));
const r = await call("GET", "/items" + (q.length ? "?" + q.join("&") : ""));
return { items: r.items || [], count: r.count || 0 };
},
async getItem(id) {
const r = await call("GET", "/items/" + encodeURIComponent(id));
return r.item;
},
// ---- write ----
async remember(item) {
const r = await call("POST", "/remember", {
content: item.content,
expert: item.expert,
scope: item.scope,
importance: item.importance,
tags: item.tags || [],
ttl: item.ttl != null ? item.ttl : null,
});
return r.item; // {id, content, expert, scope, importance, tags, …}
},
async forget(id) {
const r = await call("POST", "/forget", { id });
return !!r.deleted;
},
// ---- recall / inspect ----
async inspect(query, opts) {
opts = opts || {};
const r = await call("POST", "/inspect", {
query,
scope: opts.scope || "/",
max_tokens: opts.max_tokens || 256,
top_experts: opts.top_experts || 3,
pin_experts: opts.pin_experts || [],
});
// Normalize routing.scores (object map) -> sorted array for the UI.
const scores = Object.entries((r.routing && r.routing.scores) || {})
.map(([expert, score]) => ({ expert, score }))
.sort((a, b) => b.score - a.score);
return {
query: r.query,
routing: {
selected: (r.routing && r.routing.selected_experts) || [],
unselected: (r.routing && r.routing.unselected_experts) || [],
scores,
widened: !!(r.routing && r.routing.widened),
reason: (r.routing && r.routing.reason) || "",
},
pack: {
tokens: r.pack && r.pack.tokens,
maxTokens: r.pack && r.pack.max_tokens,
items: (r.pack && r.pack.items) || [],
dropped: (r.pack && r.pack.dropped) || [],
citations: (r.pack && r.pack.citations) || [],
prompt: (r.pack && r.pack.prompt) || "",
},
};
},
async routerExplain(query, opts) {
opts = opts || {};
const r = await call("POST", "/router/explain", {
query, scope: opts.scope || "/", top_experts: opts.top_experts || 3,
});
return {
selected: r.selected_experts || [],
unselected: r.unselected_experts || [],
scores: r.scores || [],
reason: r.reason || "",
widened: !!r.widened,
};
},
};
window.MC = MC;
})();