ruslanmv commited on
Commit
bbd26a0
·
1 Parent(s): 874d7cd

First commit

Browse files
.gitignore CHANGED
@@ -31,3 +31,4 @@ Thumbs.db
31
 
32
  # RAG index files
33
  .faiss/
 
 
31
 
32
  # RAG index files
33
  .faiss/
34
+ /backup
app/templates/base.html CHANGED
@@ -4,34 +4,208 @@
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
  <title>matrix-ai</title>
 
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
 
10
  <style>
11
- :root { --bg:#0b1220; --card:#0f172a; --muted:#94a3b8; --text:#e2e8f0; --accent:#38bdf8; }
12
- body { margin:0; font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background:var(--bg); color:var(--text); }
13
- header { padding:20px; display:flex; gap:16px; align-items:center; border-bottom:1px solid #1f2a44; }
14
- a { color: var(--accent); text-decoration: none; }
15
- .wrap { max-width: 980px; margin: 0 auto; padding: 24px; }
16
- .card { background:var(--card); border:1px solid #1f2a44; border-radius: 16px; padding: 20px; }
17
- input, textarea { width: 100%; background:#0b1220; color:var(--text); border:1px solid #1f2a44; border-radius:12px; padding:12px; font-size:14px; }
18
- button { background: var(--accent); color:#06202a; border:0; padding:10px 16px; border-radius: 12px; font-weight: 600; cursor:pointer; }
19
- pre { background:#0b1220; border:1px solid #1f2a44; padding:12px; border-radius:12px; overflow:auto; }
20
- nav a { margin-right: 16px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  </style>
22
  </head>
23
  <body>
 
 
24
  <header>
25
- <strong>matrix-ai</strong>
26
  <nav>
27
- <a href="/">Home</a>
28
  <a href="/chat">Chat</a>
29
  <a href="/dev">Dev</a>
30
  <a href="/docs" target="_blank" rel="noreferrer">API Docs</a>
31
  </nav>
32
  </header>
 
33
  <div class="wrap">
34
  {% block body %}{% endblock %}
35
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  </body>
37
- </html>
 
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
  <title>matrix-ai</title>
7
+
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Inter:wght@400;600&display=swap" rel="stylesheet">
11
+
12
  <style>
13
+ :root {
14
+ --bg: #020402;
15
+ --bg2: #071007;
16
+ --text: #c8facc;
17
+ --muted: #7ef7a7;
18
+ --matrix: #00ff9c;
19
+ --matrix-dim: #00b86b;
20
+ --card: #061006cc; /* translucent */
21
+ --border: #0d1e0f;
22
+ }
23
+
24
+ html, body { height: 100%; }
25
+ body {
26
+ margin: 0;
27
+ color: var(--text);
28
+ background:
29
+ radial-gradient(1200px 800px at 100% -10%, rgba(0,255,156,0.06), transparent 40%),
30
+ radial-gradient(1000px 600px at -10% 100%, rgba(0,255,156,0.05), transparent 40%),
31
+ linear-gradient(180deg, var(--bg), var(--bg2) 60%, var(--bg));
32
+ font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
33
+ overflow-y: auto;
34
+ }
35
+
36
+ /* Subtle scanlines */
37
+ body::after {
38
+ content: "";
39
+ position: fixed;
40
+ inset: 0;
41
+ pointer-events: none;
42
+ background: repeating-linear-gradient(
43
+ to bottom,
44
+ rgba(0, 0, 0, 0.06) 0px,
45
+ rgba(0, 0, 0, 0.06) 1px,
46
+ transparent 2px,
47
+ transparent 3px
48
+ );
49
+ mix-blend-mode: overlay;
50
+ opacity: 0.6;
51
+ z-index: 0;
52
+ }
53
+
54
+ /* Code rain canvas (behind everything) */
55
+ #code-rain {
56
+ position: fixed;
57
+ inset: 0;
58
+ width: 100vw;
59
+ height: 100vh;
60
+ z-index: 0;
61
+ pointer-events: none;
62
+ opacity: 0.65; /* keep it subtle */
63
+ }
64
+
65
+ header {
66
+ position: sticky; top: 0; z-index: 3;
67
+ display: flex; align-items: center; gap: 18px;
68
+ padding: 18px 22px;
69
+ background: linear-gradient(180deg, rgba(0,0,0,0.35), rgba(0,0,0,0));
70
+ border-bottom: 1px solid var(--border);
71
+ backdrop-filter: blur(4px);
72
+ }
73
+ .brand {
74
+ font-family: "Share Tech Mono", monospace;
75
+ color: var(--matrix);
76
+ text-shadow: 0 0 8px rgba(0,255,156,0.4);
77
+ letter-spacing: 0.04em;
78
+ font-size: 18px;
79
+ }
80
+ nav a {
81
+ color: var(--muted);
82
+ text-decoration: none;
83
+ margin-right: 16px;
84
+ transition: color .15s ease, text-shadow .15s ease;
85
+ }
86
+ nav a:hover {
87
+ color: var(--matrix);
88
+ text-shadow: 0 0 8px rgba(0,255,156,0.4);
89
+ }
90
+
91
+ .wrap { position: relative; z-index: 2; max-width: 980px; margin: 0 auto; padding: 26px 22px 60px; }
92
+ .card {
93
+ background: var(--card);
94
+ border: 1px solid var(--border);
95
+ border-radius: 16px;
96
+ box-shadow: 0 0 0 1px rgba(0,255,156,0.06), 0 8px 30px rgba(0,0,0,0.35);
97
+ padding: 20px;
98
+ }
99
+ h2, h3, h4 { font-family: "Share Tech Mono", monospace; color: var(--matrix); letter-spacing: .02em; }
100
+ p { color: var(--text); opacity: 0.95; }
101
+
102
+ input, textarea {
103
+ width: 100%;
104
+ color: var(--text);
105
+ background: #020a04;
106
+ border: 1px solid var(--border);
107
+ border-radius: 12px;
108
+ padding: 12px 12px;
109
+ font-size: 14px;
110
+ font-family: "Share Tech Mono", monospace;
111
+ outline: none;
112
+ transition: border-color .15s ease, box-shadow .15s ease;
113
+ }
114
+ input:focus, textarea:focus {
115
+ border-color: var(--matrix);
116
+ box-shadow: 0 0 0 3px rgba(0,255,156,0.08), 0 0 12px rgba(0,255,156,0.25) inset;
117
+ }
118
+
119
+ button {
120
+ background: linear-gradient(180deg, #00ff9c, #00c97e);
121
+ color: #002f1b;
122
+ border: 0;
123
+ padding: 10px 16px;
124
+ border-radius: 12px;
125
+ font-weight: 700;
126
+ font-family: "Share Tech Mono", monospace;
127
+ letter-spacing: 0.03em;
128
+ cursor: pointer;
129
+ box-shadow: 0 6px 20px rgba(0,255,156,0.25);
130
+ transition: transform .05s ease, box-shadow .15s ease, filter .15s ease;
131
+ }
132
+ button:hover { filter: brightness(1.05); box-shadow: 0 10px 30px rgba(0,255,156,0.35); }
133
+ button:active { transform: translateY(1px); }
134
+
135
+ pre, code {
136
+ font-family: "Share Tech Mono", monospace;
137
+ background: #020a04;
138
+ border: 1px solid var(--border);
139
+ border-radius: 12px;
140
+ }
141
+ pre { padding: 12px; overflow: auto; }
142
+
143
+ @keyframes glow {
144
+ 0%, 100% { text-shadow: 0 0 10px rgba(0,255,156,0.12); }
145
+ 50% { text-shadow: 0 0 14px rgba(0,255,156,0.28); }
146
+ }
147
+ h3 { animation: glow 3.5s ease-in-out infinite; }
148
  </style>
149
  </head>
150
  <body>
151
+ <canvas id="code-rain"></canvas>
152
+
153
  <header>
154
+ <div class="brand">MATRIX-AI</div>
155
  <nav>
 
156
  <a href="/chat">Chat</a>
157
  <a href="/dev">Dev</a>
158
  <a href="/docs" target="_blank" rel="noreferrer">API Docs</a>
159
  </nav>
160
  </header>
161
+
162
  <div class="wrap">
163
  {% block body %}{% endblock %}
164
  </div>
165
+
166
+ <script>
167
+ // Lightweight "Matrix" code rain — tuned for Spaces
168
+ (function () {
169
+ const c = document.getElementById('code-rain');
170
+ const ctx = c.getContext('2d', { alpha: true });
171
+ let w, h, cols, drops;
172
+ const fontSize = 20; // <-- CHANGED: Increased from 16 to 20
173
+ const charSet = 'アァカサタナハマヤラワ0123456789アイウエオアイウエオ01';
174
+
175
+ function resize() {
176
+ w = c.width = window.innerWidth;
177
+ h = c.height = window.innerHeight;
178
+ cols = Math.floor(w / fontSize);
179
+ drops = Array(cols).fill(0).map(() => Math.floor(Math.random() * -50));
180
+ ctx.font = fontSize + "px 'Share Tech Mono', monospace";
181
+ }
182
+
183
+ function draw() {
184
+ // translucent rect to create trail effect
185
+ ctx.fillStyle = 'rgba(2, 10, 4, 0.10)';
186
+ ctx.fillRect(0, 0, w, h);
187
+
188
+ for (let i = 0; i < cols; i++) {
189
+ const x = i * fontSize;
190
+ const y = drops[i] * fontSize;
191
+ const ch = charSet[Math.floor(Math.random() * charSet.length)];
192
+ // leading glow + core
193
+ ctx.shadowColor = 'rgba(0,255,156,0.35)';
194
+ ctx.shadowBlur = 8;
195
+ ctx.fillStyle = '#00ff9c';
196
+ ctx.fillText(ch, x, y);
197
+
198
+ // advance
199
+ if (y > h && Math.random() > 0.975) drops[i] = 0;
200
+ else drops[i]++;
201
+ }
202
+ setTimeout(draw, 70); // <-- CHANGED: Replaced requestAnimationFrame for slower speed
203
+ }
204
+
205
+ window.addEventListener('resize', resize);
206
+ resize();
207
+ draw(); // Start the animation
208
+ })();
209
+ </script>
210
  </body>
211
+ </html>
app/templates/chat.html CHANGED
@@ -1,14 +1,129 @@
1
  {% extends "base.html" %}
2
  {% block body %}
3
  <div class="card">
4
- <h3>Chat about Matrix System 1.0</h3>
5
- <form method="post" style="display:grid; gap:12px;">
6
- <textarea name="question" rows="4" placeholder="Ask anything about the Matrix EcoSystem, Guardian, or Hub...">{{ question or '' }}</textarea>
7
- <div><button type="submit">Ask</button></div>
 
 
 
 
 
 
 
 
 
 
8
  </form>
9
- {% if answer %}
10
- <h4>Answer</h4>
11
- <pre>{{ answer }}</pre>
12
- {% endif %}
13
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  {% endblock %}
 
1
  {% extends "base.html" %}
2
  {% block body %}
3
  <div class="card">
4
+ <h3>Chat Matrix System 1.0</h3>
5
+
6
+ <!-- Messages -->
7
+ <div id="messages" style="margin-top:14px; display:flex; flex-direction:column; gap:10px; max-height:60vh; overflow:auto;">
8
+ <!-- Filled by JS from localStorage -->
9
+ </div>
10
+
11
+ <!-- Composer -->
12
+ <form id="chatForm" style="display:grid; gap:12px; margin-top:14px;">
13
+ <textarea id="question" rows="4" placeholder="Ask anything about Matrix EcoSystem, Guardian, or Hub..."></textarea>
14
+ <div style="display:flex; gap:10px; align-items:center;">
15
+ <button id="sendBtn" type="submit">Send</button>
16
+ <button id="clearBtn" type="button" style="background:#0c1d13; color:#7ef7a7; box-shadow:none; border:1px solid var(--border);">Clear</button>
17
+ </div>
18
  </form>
 
 
 
 
19
  </div>
20
+
21
+ <style>
22
+ .bubble {
23
+ max-width: 80%;
24
+ border: 1px solid var(--border);
25
+ border-radius: 14px;
26
+ padding: 10px 12px;
27
+ line-height: 1.45;
28
+ white-space: pre-wrap;
29
+ word-break: break-word;
30
+ box-shadow: 0 4px 16px rgba(0,0,0,0.25), 0 0 0 1px rgba(0,255,156,0.05);
31
+ font-family: "Share Tech Mono", monospace;
32
+ }
33
+ .user { align-self: flex-end; background: #062013; color: var(--text); border-color: #0e2e1a; }
34
+ .bot { align-self: flex-start; background: #05140c; color: var(--text); border-color: #0c2416; }
35
+ .meta { font-size: 11px; opacity: .6; margin-top: 2px; }
36
+ </style>
37
+
38
+ <script>
39
+ (function () {
40
+ const KEY = 'matrix_ai_chat_history';
41
+ const messagesEl = document.getElementById('messages');
42
+ const form = document.getElementById('chatForm');
43
+ const input = document.getElementById('question');
44
+ const sendBtn = document.getElementById('sendBtn');
45
+ const clearBtn = document.getElementById('clearBtn');
46
+
47
+ function loadHistory() {
48
+ try {
49
+ return JSON.parse(localStorage.getItem(KEY) || '[]');
50
+ } catch { return []; }
51
+ }
52
+ function saveHistory(hist) {
53
+ localStorage.setItem(KEY, JSON.stringify(hist.slice(-100))); // cap
54
+ }
55
+ function msgEl(role, text, ts) {
56
+ const wrap = document.createElement('div');
57
+ wrap.style.display = 'flex';
58
+ wrap.style.flexDirection = 'column';
59
+ wrap.style.gap = '2px';
60
+ const b = document.createElement('div');
61
+ b.className = 'bubble ' + (role === 'user' ? 'user' : 'bot');
62
+ b.textContent = text;
63
+ const meta = document.createElement('div');
64
+ meta.className = 'meta';
65
+ meta.textContent = new Date(ts).toLocaleString();
66
+ wrap.appendChild(b);
67
+ wrap.appendChild(meta);
68
+ return wrap;
69
+ }
70
+ function render(hist) {
71
+ messagesEl.innerHTML = '';
72
+ hist.forEach(m => messagesEl.appendChild(msgEl(m.role, m.text, m.ts)));
73
+ messagesEl.scrollTop = messagesEl.scrollHeight;
74
+ }
75
+
76
+ let history = loadHistory();
77
+ if (history.length === 0) {
78
+ // seed a friendly welcome
79
+ history.push({ role: 'bot', text: 'Welcome to MATRIX-AI. Ask me about Matrix System 1.0, Guardian, or the Hub.', ts: Date.now() });
80
+ saveHistory(history);
81
+ }
82
+ render(history);
83
+
84
+ clearBtn.addEventListener('click', () => {
85
+ history = [];
86
+ saveHistory(history);
87
+ render(history);
88
+ });
89
+
90
+ form.addEventListener('submit', async (e) => {
91
+ e.preventDefault();
92
+ const q = (input.value || '').trim();
93
+ if (!q) return;
94
+ input.value = '';
95
+ sendBtn.disabled = true;
96
+
97
+ // append user message
98
+ const userMsg = { role: 'user', text: q, ts: Date.now() };
99
+ history.push(userMsg);
100
+ saveHistory(history);
101
+ render(history);
102
+
103
+ try {
104
+ const port = (window.location.port || (window.location.href.includes('/+/') ? '' : ''));
105
+ const base = window.location.origin; // same origin (HF proxies handle it)
106
+ const r = await fetch(base.replace(/\/+$/, '') + '/v1/chat', {
107
+ method: 'POST',
108
+ headers: { 'content-type': 'application/json' },
109
+ body: JSON.stringify({ query: q })
110
+ });
111
+ let answer = '(no answer)';
112
+ if (r.ok) {
113
+ const data = await r.json();
114
+ answer = (data && (data.answer || data.response || JSON.stringify(data))) || answer;
115
+ } else {
116
+ answer = `HTTP ${r.status}`;
117
+ }
118
+ history.push({ role: 'bot', text: answer, ts: Date.now() });
119
+ } catch (err) {
120
+ history.push({ role: 'bot', text: 'Error: ' + (err && err.message ? err.message : String(err)), ts: Date.now() });
121
+ } finally {
122
+ saveHistory(history);
123
+ render(history);
124
+ sendBtn.disabled = false;
125
+ }
126
+ });
127
+ })();
128
+ </script>
129
  {% endblock %}
app/templates/dev.html CHANGED
@@ -1,12 +1,10 @@
1
  {% extends "base.html" %}
2
  {% block body %}
3
  <div class="card">
4
- <h3>Dev Exercise /v1/plan</h3>
5
- <form method="post" style="display:grid; gap:12px;">
6
- <textarea name="payload" rows="16" spellcheck="false">{{ sample }}</textarea>
7
- <div style="display:flex; gap:8px;">
8
- <button type="submit">Call /v1/plan</button>
9
- </div>
10
  </form>
11
 
12
  {% if error %}
 
1
  {% extends "base.html" %}
2
  {% block body %}
3
  <div class="card">
4
+ <h3>Dev Exercise /v1/plan</h3>
5
+ <form method="post" style="display:grid; gap:12px; margin-top:12px;">
6
+ <textarea name="payload" rows="18" spellcheck="false">{{ sample }}</textarea>
7
+ <div><button type="submit">Call /v1/plan</button></div>
 
 
8
  </form>
9
 
10
  {% if error %}
app/ui.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import APIRouter, Request, Form
2
- from fastapi.responses import HTMLResponse
3
  from fastapi.templating import Jinja2Templates
4
  import httpx, os, json
5
 
@@ -7,13 +7,13 @@ router = APIRouter()
7
  templates = Jinja2Templates(directory="app/templates")
8
 
9
  def _self_base_url() -> str:
10
- # When running inside HF Space Docker, use localhost + PORT
11
  port = os.getenv("PORT", "7860")
12
  return f"http://127.0.0.1:{port}"
13
 
14
- @router.get("/", response_class=HTMLResponse)
15
- async def home(request: Request):
16
- return templates.TemplateResponse("home.html", {"request": request})
 
17
 
18
  @router.get("/chat", response_class=HTMLResponse)
19
  async def chat_get(request: Request):
@@ -21,11 +21,10 @@ async def chat_get(request: Request):
21
 
22
  @router.post("/chat", response_class=HTMLResponse)
23
  async def chat_post(request: Request, question: str = Form(...)):
24
- # Call your /v1/chat (or return a placeholder)
25
- base_url = _self_base_url()
26
  try:
27
- async with httpx.AsyncClient(timeout=15.0) as client:
28
- r = await client.post("/v1/chat", base_url=base_url, json={"query": question})
 
29
  data = r.json()
30
  answer = data.get("answer", "(no answer)")
31
  except Exception as e:
@@ -34,7 +33,6 @@ async def chat_post(request: Request, question: str = Form(...)):
34
 
35
  @router.get("/dev", response_class=HTMLResponse)
36
  async def dev_get(request: Request):
37
- # Prefill a realistic plan request used by Matrix-Guardian
38
  sample = {
39
  "context": {
40
  "entity_uid": "matrix-ai",
@@ -49,14 +47,14 @@ async def dev_get(request: Request):
49
 
50
  @router.post("/dev", response_class=HTMLResponse)
51
  async def dev_post(request: Request, payload: str = Form(...)):
52
- base_url = _self_base_url()
53
  try:
54
  body = json.loads(payload)
55
  except Exception as e:
56
  return templates.TemplateResponse("dev.html", {"request": request, "sample": payload, "error": f"Invalid JSON: {e}"})
 
57
  try:
58
- async with httpx.AsyncClient(timeout=15.0) as client:
59
- r = await client.post("/v1/plan", base_url=base_url, json=body)
60
  r.raise_for_status()
61
  data = r.json()
62
  pretty = json.dumps(data, indent=2)
 
1
  from fastapi import APIRouter, Request, Form
2
+ from fastapi.responses import HTMLResponse, RedirectResponse
3
  from fastapi.templating import Jinja2Templates
4
  import httpx, os, json
5
 
 
7
  templates = Jinja2Templates(directory="app/templates")
8
 
9
  def _self_base_url() -> str:
 
10
  port = os.getenv("PORT", "7860")
11
  return f"http://127.0.0.1:{port}"
12
 
13
+ @router.get("/", include_in_schema=False)
14
+ async def home_redirect():
15
+ # Default to the Chat page
16
+ return RedirectResponse(url="/chat", status_code=302)
17
 
18
  @router.get("/chat", response_class=HTMLResponse)
19
  async def chat_get(request: Request):
 
21
 
22
  @router.post("/chat", response_class=HTMLResponse)
23
  async def chat_post(request: Request, question: str = Form(...)):
 
 
24
  try:
25
+ async with httpx.AsyncClient(base_url=_self_base_url(), timeout=15.0) as client:
26
+ r = await client.post("/v1/chat", json={"query": question})
27
+ r.raise_for_status()
28
  data = r.json()
29
  answer = data.get("answer", "(no answer)")
30
  except Exception as e:
 
33
 
34
  @router.get("/dev", response_class=HTMLResponse)
35
  async def dev_get(request: Request):
 
36
  sample = {
37
  "context": {
38
  "entity_uid": "matrix-ai",
 
47
 
48
  @router.post("/dev", response_class=HTMLResponse)
49
  async def dev_post(request: Request, payload: str = Form(...)):
 
50
  try:
51
  body = json.loads(payload)
52
  except Exception as e:
53
  return templates.TemplateResponse("dev.html", {"request": request, "sample": payload, "error": f"Invalid JSON: {e}"})
54
+
55
  try:
56
+ async with httpx.AsyncClient(base_url=_self_base_url(), timeout=15.0) as client:
57
+ r = await client.post("/v1/plan", json=body)
58
  r.raise_for_status()
59
  data = r.json()
60
  pretty = json.dumps(data, indent=2)