File size: 3,901 Bytes
ce45eb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/* Matrix Context — live API adapter (same-origin /v1, MoC Contract v1).
 * Maps backend shapes for the UI: remember->item, inspect->routing/.pack,
 * version->contract_version/implementation_version, scopes->plain strings.
 */
(function () {
  const BASE = location.origin + "/v1";
  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 { status: 0, message: "network error — is the backend running?" }; }
    let data = {}; try { data = await resp.json(); } catch (e) {}
    if (!resp.ok) throw { status: resp.status, message: (data && data.error) || ("HTTP " + resp.status) };
    return data;
  }

  window.MC = {
    base: BASE, setToken, hasToken: () => !!TOKEN,
    async health() { return await call("GET", "/health"); },
    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 }));
    },
    async items(f) {
      f = f || {}; const q = [];
      if (f.scope) q.push("scope=" + encodeURIComponent(f.scope));
      if (f.expert) q.push("expert=" + encodeURIComponent(f.expert));
      const r = await call("GET", "/items" + (q.length ? "?" + q.join("&") : ""));
      return { items: r.items || [], count: r.count || 0 };
    },
    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;
    },
    async forget(id) { return !!(await call("POST", "/forget", { id })).deleted; },
    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 || [],
      });
      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 };
    },
  };
})();