Spaces:
Sleeping
Sleeping
File size: 6,417 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | """Matrix Context — frontend / control-plane server.
A self-contained, Hugging-Face-deployable launcher that serves:
* the static control-plane UI in ``frontend/app/`` (Overview, Inspector,
Builder, Memory, Experts, Routing, Benchmarks, Standard, Settings), and
* the live MoC Contract v1 API under ``/v1`` — by reusing the published
backend's ``RestService`` / ``dispatch``.
This is **additive and non-destructive**: it imports ``matrix_context`` and the
existing REST app; it does not modify them. A small demo memory set is seeded on
startup so the Space is useful immediately.
python frontend/server.py # local -> http://127.0.0.1:7860
PORT=7860 HOST=0.0.0.0 python ... # container / Hugging Face Space
"""
from __future__ import annotations
import json
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from urllib.parse import parse_qs, urlparse
from matrix_context import ContextManager
from matrix_context.serve.rest.app import RestService, dispatch
APP_DIR = Path(__file__).parent / "app"
HOST = os.environ.get("HOST", "127.0.0.1")
PORT = int(os.environ.get("PORT", "7860"))
DB_PATH = os.environ.get("MATRIX_CONTEXT_PATH", ":memory:")
_CONTENT_TYPES = {".html": "text/html; charset=utf-8",
".js": "application/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".svg": "image/svg+xml", ".json": "application/json",
".png": "image/png", ".ico": "image/x-icon"}
# Demo seed — a small, typed, multi-scope memory set so every view is populated.
SEED = [
("Decision: use SQLite as the default storage backend; vectors accelerate, SQL is the source of truth.",
"semantic", "project:matrix-context", 0.95, ["backend", "sqlite", "type:decision"]),
("Matrix Context exposes both a Python SDK and an MCP server so agents and operators share one memory plane.",
"semantic", "project:matrix-context", 0.9, ["mcp", "sdk", "type:decision"]),
("Policy: the agent must never store API keys, tokens, or PII in durable memory.",
"policy", "project:matrix-context", 0.95, ["security", "pii", "type:rule"]),
("Decision: defer Milvus support to v2; pgvector lands in v1.",
"episodic", "project:matrix-context", 0.8, ["milvus", "roadmap", "type:decision"]),
("The user prefers local-first tools over cloud services by default.",
"profile", "user:42", 0.9, ["preference", "type:preference"]),
("The user prefers concise, direct answers with code first.",
"profile", "user:42", 0.8, ["preference", "tone", "type:preference"]),
("The whitepaper reports that routed typed context cuts distractors at equal recall.",
"document", "project:matrix-context", 0.7, ["whitepaper", "type:document"]),
("Decision: ship the REST API and Context Console before the MCP server.",
"semantic", "project:acme", 0.85, ["rest", "console", "type:decision"]),
("Policy in project:acme — audit-log every write to profile or policy memory.",
"policy", "project:acme", 0.88, ["audit", "governance", "type:rule"]),
("In the last session we agreed the demo should run on Hugging Face Spaces.",
"episodic", "project:acme", 0.65, ["session", "demo", "type:episode"]),
]
def build_service() -> RestService:
mgr = ContextManager.create("matrix-context-demo", path=DB_PATH)
if not mgr.store.all_items():
for content, expert, scope, importance, tags in SEED:
mgr.remember(content, expert=expert, scope=scope,
importance=importance, tags=tags)
return RestService(mgr)
def _static(path: str):
"""Resolve a static asset under APP_DIR (path-traversal safe)."""
rel = path.lstrip("/") or "index.html"
target = (APP_DIR / rel).resolve()
if APP_DIR.resolve() not in target.parents and target != APP_DIR.resolve():
return None
if not target.is_file():
return None
ctype = _CONTENT_TYPES.get(target.suffix, "application/octet-stream")
return target.read_bytes(), ctype
def make_handler(service: RestService):
class Handler(BaseHTTPRequestHandler):
def log_message(self, *a):
pass
def _send_json(self, status, payload):
data = json.dumps(payload).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def _send_bytes(self, data, ctype, status=200):
self.send_response(status)
self.send_header("Content-Type", ctype)
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def _read_body(self):
n = int(self.headers.get("Content-Length") or 0)
if not n:
return {}
try:
obj = json.loads(self.rfile.read(n) or b"{}")
return obj if isinstance(obj, dict) else {}
except json.JSONDecodeError:
return {}
def do_GET(self):
p = urlparse(self.path)
if p.path.startswith("/v1"):
st, payload = dispatch(service, "GET", p.path, query=parse_qs(p.query))
self._send_json(st, payload)
return
asset = _static("index.html" if p.path == "/" else p.path)
if asset:
self._send_bytes(*asset)
else:
self._send_json(404, {"error": f"not found: {p.path}"})
def do_POST(self):
p = urlparse(self.path)
if p.path.startswith("/v1"):
st, payload = dispatch(service, "POST", p.path, body=self._read_body())
self._send_json(st, payload)
else:
self._send_json(404, {"error": f"not found: {p.path}"})
return Handler
def main() -> int:
service = build_service()
httpd = HTTPServer((HOST, PORT), make_handler(service))
n = len(service.m.store.all_items())
print(f"Matrix Context control plane on http://{HOST}:{PORT} ({n} seeded items)")
try:
httpd.serve_forever()
except KeyboardInterrupt:
httpd.server_close()
return 0
if __name__ == "__main__":
raise SystemExit(main())
|