ranranrunforit commited on
Commit
9b99d45
·
verified ·
1 Parent(s): a946183

Upload 33 files

Browse files
server.py CHANGED
@@ -201,6 +201,7 @@ async def automation_state():
201
  "log": automation.STATE.get("log", [])[-40:],
202
  "schedule": automation.schedule_info(),
203
  "traces": research_agent.list_traces(),
 
204
  })
205
 
206
 
@@ -222,9 +223,31 @@ async def model_status():
222
  })
223
 
224
 
 
 
 
225
  @app.post("/api/model/test")
226
  async def model_test():
227
- return JSONResponse({"result": llm_local.quick_test()})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
 
230
  @app.get("/api/model/finetune-status")
 
201
  "log": automation.STATE.get("log", [])[-40:],
202
  "schedule": automation.schedule_info(),
203
  "traces": research_agent.list_traces(),
204
+ "running": bool(automation.STATE.get("running")),
205
  })
206
 
207
 
 
223
  })
224
 
225
 
226
+ _SELFTEST = {"running": False, "result": ""}
227
+
228
+
229
  @app.post("/api/model/test")
230
  async def model_test():
231
+ import threading
232
+ if _SELFTEST["running"]:
233
+ return JSONResponse({"started": True, "running": True})
234
+
235
+ def _run():
236
+ _SELFTEST["running"] = True
237
+ _SELFTEST["result"] = ""
238
+ try:
239
+ _SELFTEST["result"] = llm_local.quick_test()
240
+ except Exception as e:
241
+ _SELFTEST["result"] = f"❌ {e}"
242
+ _SELFTEST["running"] = False
243
+
244
+ threading.Thread(target=_run, daemon=True).start()
245
+ return JSONResponse({"started": True, "running": True})
246
+
247
+
248
+ @app.get("/api/model/test-status")
249
+ async def model_test_status():
250
+ return JSONResponse({"running": _SELFTEST["running"], "result": _SELFTEST["result"]})
251
 
252
 
253
  @app.get("/api/model/finetune-status")
ui_kits/chan-compass/api.js CHANGED
@@ -56,6 +56,7 @@ window.CCApi = (function () {
56
  publishTraces: (repo) => jpost("/api/automation/publish-traces", { repo }),
57
  modelStatus: () => jget("/api/model/status"),
58
  modelTest: () => jpost("/api/model/test", {}),
 
59
  finetuneStatus: () => jget("/api/model/finetune-status"),
60
  exportDataset: () => jpost("/api/model/export-dataset", {}),
61
  email: (content, to, tag) => jpost("/api/email", { content, to, tag }),
 
56
  publishTraces: (repo) => jpost("/api/automation/publish-traces", { repo }),
57
  modelStatus: () => jget("/api/model/status"),
58
  modelTest: () => jpost("/api/model/test", {}),
59
+ modelTestStatus: () => jget("/api/model/test-status"),
60
  finetuneStatus: () => jget("/api/model/finetune-status"),
61
  exportDataset: () => jpost("/api/model/export-dataset", {}),
62
  email: (content, to, tag) => jpost("/api/email", { content, to, tag }),
ui_kits/chan-compass/app.jsx CHANGED
@@ -3,9 +3,28 @@ const _DS = window.ChanCompassSpectrum2DesignSystem_b52b37;
3
  const { Tabs: CCTabs, StatusLight: CCStatus, Badge: CCBadge } = _DS;
4
  const { useState: useS, useEffect: useE } = React;
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  function Glyph({ name, size = 18, color, style }) {
7
- useE(() => { if (window.lucide) window.lucide.createIcons(); });
8
- return <i data-lucide={name} style={{ width:size, height:size, color, display:'inline-flex', ...style }}></i>;
 
 
9
  }
10
 
11
  /* Error boundary — a render error in any view shows a message instead of
 
3
  const { Tabs: CCTabs, StatusLight: CCStatus, Badge: CCBadge } = _DS;
4
  const { useState: useS, useEffect: useE } = React;
5
 
6
+ function _pascalG(name) {
7
+ return String(name).split(/[-_]/).map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join("");
8
+ }
9
+ function _glyphSvg(name, size, color) {
10
+ const L = window.lucide;
11
+ if (!L || !L.icons) return "";
12
+ const node = L.icons[_pascalG(name)] || L.icons[name];
13
+ if (!node) return "";
14
+ const children = Array.isArray(node) ? node : (node.tags || node[2] || []);
15
+ const inner = (children || []).map(([tag, attrs]) => {
16
+ const a = Object.entries(attrs||{}).map(([k,v])=>`${k}="${v}"`).join(" ");
17
+ return `<${tag} ${a}/>`;
18
+ }).join("");
19
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" `
20
+ + `viewBox="0 0 24 24" fill="none" stroke="${color||'currentColor'}" `
21
+ + `stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${inner}</svg>`;
22
+ }
23
  function Glyph({ name, size = 18, color, style }) {
24
+ const html = _glyphSvg(name, size, color);
25
+ return <span style={{ width:size, height:size, display:'inline-flex',
26
+ alignItems:'center', justifyContent:'center', ...style }}
27
+ dangerouslySetInnerHTML={{__html: html}}/>;
28
  }
29
 
30
  /* Error boundary — a render error in any view shows a message instead of
ui_kits/chan-compass/views.jsx CHANGED
@@ -5,9 +5,30 @@ const { Button, Field, Checkbox, Switch, Card, InlineAlert, Badge, StatusLight,
5
  const { useState, useEffect, useRef } = React;
6
  const API = window.CCApi;
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  function Icon({ name, size = 18, color, style }) {
9
- useEffect(() => { if (window.lucide) window.lucide.createIcons(); });
10
- return <i data-lucide={name} style={{ width:size, height:size, color, display:'inline-flex', ...style }}></i>;
 
 
11
  }
12
 
13
  const num = (v) => (typeof v === "number" ? v : parseFloat(v));
@@ -281,24 +302,27 @@ function ResearchView() {
281
 
282
  /* ─────────── Automation ─────────── */
283
  function AutomationView() {
284
- const [state, setState] = useState({log:[], schedule:"", traces:""});
285
  const [repo, setRepo] = useState("ranranrunforit/chan-compass-agent-traces");
286
  const [pubStatus, setPubStatus] = useState("");
287
- const [running, setRunning] = useState(false);
288
  useEffect(()=>{
289
- const tick = ()=> API.autoState().then(setState).catch(()=>{});
290
- tick(); const id=setInterval(tick, 2500); return ()=>clearInterval(id);
291
  },[]);
292
- const runNow = async () => { setRunning(true); await API.autoRun().catch(()=>{}); setRunning(false); };
 
293
  const publish = async () => { setPubStatus("Publishing...");
294
  try { const r = await API.publishTraces(repo); setPubStatus(r.status); }
295
  catch(e){ setPubStatus("X "+e.message); } };
296
  return (
297
  <div style={{display:"flex", flexDirection:"column", gap:"var(--space-300)"}}>
298
- <InlineAlert variant="informative" title="Daily pipeline">{state.schedule}</InlineAlert>
299
- <Card title="Pipeline" subtitle="data, signals, rotation, news, auto-research"
300
- action={<Button variant="accent" size="sm" onClick={runNow} disabled={running}>
301
- <Icon name="zap" size={14}/> {running?"Running...":"Run now"}</Button>}>
 
 
302
  <div className="cc-log" style={{maxHeight:300, overflow:"auto"}}>
303
  {(state.log||[]).map((l,i)=><div key={i} className="mono" style={{fontSize:12}}>{l}</div>)}
304
  {(!state.log || !state.log.length) && <span className="dim">(no log yet)</span>}
@@ -327,7 +351,16 @@ function ModelView() {
327
  tick(); const id=setInterval(tick, 3000); return ()=>clearInterval(id);
328
  },[]);
329
  useEffect(()=>{ API.finetuneStatus().then(r=>setFt(r.status)).catch(()=>{}); },[]);
330
- const runTest = async () => { setTest("Testing..."); const r=await API.modelTest(); setTest(r.result); };
 
 
 
 
 
 
 
 
 
331
  const [dlUrl, setDlUrl] = useState("");
332
  const exportDs = async () => {
333
  const r = await API.exportDataset();
@@ -335,7 +368,7 @@ function ModelView() {
335
  else { setDl("No pairs captured yet — run a few Signals AI summaries first."); setDlUrl(""); }
336
  };
337
  return (
338
- <div style={{display:"flex", flexDirection:"column", gap:"var(--space-300)", maxWidth:760}}>
339
  <InlineAlert variant="informative" title="Everything runs locally through llama.cpp">
340
  Qwen3 GGUF weights, all below the 32B cap. The Summary sub-agent uses a published fine-tune (Chan-Tuned Qwen3-1.7B).
341
  </InlineAlert>
 
5
  const { useState, useEffect, useRef } = React;
6
  const API = window.CCApi;
7
 
8
+ // kebab-case (data-lucide name) → PascalCase (lucide.icons key)
9
+ function _pascal(name) {
10
+ return String(name).split(/[-_]/).map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join("");
11
+ }
12
+ function _lucideSvg(name, size, color) {
13
+ const L = window.lucide;
14
+ if (!L || !L.icons) return "";
15
+ const node = L.icons[_pascal(name)] || L.icons[name];
16
+ if (!node) return "";
17
+ // lucide icon data is an array of [tag, attrs] children
18
+ const children = Array.isArray(node) ? node : (node.tags || node[2] || []);
19
+ const inner = (children || []).map(([tag, attrs]) => {
20
+ const a = Object.entries(attrs||{}).map(([k,v])=>`${k}="${v}"`).join(" ");
21
+ return `<${tag} ${a}/>`;
22
+ }).join("");
23
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" `
24
+ + `viewBox="0 0 24 24" fill="none" stroke="${color||'currentColor'}" `
25
+ + `stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${inner}</svg>`;
26
+ }
27
  function Icon({ name, size = 18, color, style }) {
28
+ const html = _lucideSvg(name, size, color);
29
+ return <span style={{ width:size, height:size, display:"inline-flex",
30
+ alignItems:"center", justifyContent:"center", ...style }}
31
+ dangerouslySetInnerHTML={{__html: html}}/>;
32
  }
33
 
34
  const num = (v) => (typeof v === "number" ? v : parseFloat(v));
 
302
 
303
  /* ─────────── Automation ─────────── */
304
  function AutomationView() {
305
+ const [state, setState] = useState({log:[], schedule:"", traces:"", running:false});
306
  const [repo, setRepo] = useState("ranranrunforit/chan-compass-agent-traces");
307
  const [pubStatus, setPubStatus] = useState("");
308
+ const [starting, setStarting] = useState(false);
309
  useEffect(()=>{
310
+ const tick = ()=> API.autoState().then(s=>{ setState(s); if (s.running) setStarting(false); }).catch(()=>{});
311
+ tick(); const id=setInterval(tick, 2000); return ()=>clearInterval(id);
312
  },[]);
313
+ const busy = starting || state.running;
314
+ const runNow = async () => { setStarting(true); await API.autoRun().catch(()=>setStarting(false)); };
315
  const publish = async () => { setPubStatus("Publishing...");
316
  try { const r = await API.publishTraces(repo); setPubStatus(r.status); }
317
  catch(e){ setPubStatus("X "+e.message); } };
318
  return (
319
  <div style={{display:"flex", flexDirection:"column", gap:"var(--space-300)"}}>
320
+ <Card title="Daily pipeline" subtitle="schedule + status">
321
+ <Markdown text={state.schedule||""}/>
322
+ </Card>
323
+ <Card title="Pipeline" subtitle="data → signals → rotation → news → auto-research"
324
+ action={<Button variant="accent" size="sm" onClick={runNow} disabled={busy}>
325
+ <Icon name="zap" size={14}/> {busy?"Running…":"Run now"}</Button>}>
326
  <div className="cc-log" style={{maxHeight:300, overflow:"auto"}}>
327
  {(state.log||[]).map((l,i)=><div key={i} className="mono" style={{fontSize:12}}>{l}</div>)}
328
  {(!state.log || !state.log.length) && <span className="dim">(no log yet)</span>}
 
351
  tick(); const id=setInterval(tick, 3000); return ()=>clearInterval(id);
352
  },[]);
353
  useEffect(()=>{ API.finetuneStatus().then(r=>setFt(r.status)).catch(()=>{}); },[]);
354
+ const runTest = async () => {
355
+ setTest("⏳ Testing sub-agents… (this can take a moment on CPU)");
356
+ await API.modelTest();
357
+ const poll = setInterval(async () => {
358
+ try {
359
+ const s = await API.modelTestStatus();
360
+ if (!s.running && s.result) { setTest(s.result); clearInterval(poll); }
361
+ } catch (_) { clearInterval(poll); }
362
+ }, 1500);
363
+ };
364
  const [dlUrl, setDlUrl] = useState("");
365
  const exportDs = async () => {
366
  const r = await API.exportDataset();
 
368
  else { setDl("No pairs captured yet — run a few Signals AI summaries first."); setDlUrl(""); }
369
  };
370
  return (
371
+ <div style={{display:"flex", flexDirection:"column", gap:"var(--space-300)"}}>
372
  <InlineAlert variant="informative" title="Everything runs locally through llama.cpp">
373
  Qwen3 GGUF weights, all below the 32B cap. The Summary sub-agent uses a published fine-tune (Chan-Tuned Qwen3-1.7B).
374
  </InlineAlert>