File size: 11,761 Bytes
4fd620e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4bb3f7c
 
 
4fd620e
 
 
 
 
 
 
56e2384
4fd620e
4bb3f7c
 
 
 
 
 
 
 
 
 
 
4fd620e
 
 
 
 
4cf5cdc
 
4fd620e
 
 
 
 
4bb3f7c
 
 
 
 
4fd620e
 
 
4bb3f7c
 
b9cbc2e
4cf5cdc
b9cbc2e
4cf5cdc
 
b9cbc2e
 
4bb3f7c
b9cbc2e
3dbd21f
 
9ae1216
 
56e2384
4cf5cdc
4bb3f7c
 
4fd620e
 
4cf5cdc
4fd620e
 
 
 
 
 
 
 
 
 
4cf5cdc
 
 
 
 
 
 
 
 
 
 
4fd620e
 
 
 
4cf5cdc
4fd620e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b9cbc2e
4fd620e
 
 
 
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DaisyChain-Web — train by opening a page</title>
<style>
  :root {
    --page-bg: #efe4c9;
    --card-bg: #fbf6e8;
    --card-border: rgba(139, 111, 71, 0.30);
    --text: #2a1d0a;
    --text-soft: #6b4423;
    --accent: #4a7c2e;
    --accent-deep: #2d5016;
    --counter-bg: linear-gradient(135deg, #2d5016 0%, #1f3a0f 100%);
    --counter-num: #f5ecd9;
    --counter-label: #c9b072;
    --btn: linear-gradient(135deg, #4a7c2e 0%, #2d5016 100%);
    --btn-hover: linear-gradient(135deg, #5a8c3e 0%, #3d6020 100%);
    --warn: #8b2e25;
    --link: #4a7c2e;
    --track: rgba(139, 111, 71, 0.22);
  }
  @media (prefers-color-scheme: dark) {
    :root {
      --page-bg: #14100a; --card-bg: #1f1a12; --card-border: rgba(201, 176, 114, 0.35);
      --text: #ede1c3; --text-soft: #c9b072; --accent: #9bc466; --accent-deep: #6b9039;
      --counter-bg: linear-gradient(135deg, #1a2e0d 0%, #0c1606 100%);
      --counter-label: #c9b072; --warn: #ff9b8e; --link: #9bc466; --track: rgba(201,176,114,0.20);
    }
  }
  * { box-sizing: border-box; }
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
         max-width: 720px; margin: 0 auto; padding: 24px 18px 40px; line-height: 1.5;
         background: var(--page-bg); color: var(--text); }
  h1 { margin: 0 0 2px; font-size: 1.7rem; letter-spacing: .3px; }
  .sub { color: var(--text-soft); margin: 0 0 18px; font-size: .92rem; }
  .sub code { background: rgba(74,124,46,.12); padding: 1px 5px; border-radius: 4px; }
  .card { background: var(--card-bg); border: 1px solid var(--card-border);
          border-radius: 10px; padding: 14px 16px; margin: 12px 0;
          box-shadow: 0 2px 10px rgba(0,0,0,0.06); }
  .lbl { color: var(--text-soft); font-size: 11px; font-weight: 800; letter-spacing: 1.5px;
         text-transform: uppercase; margin-bottom: 8px; }
  .row { display: flex; justify-content: space-between; gap: 12px; padding: 3px 0; font-size: .95rem; }
  .k { color: var(--text-soft); } .v { font-weight: 700; text-align: right; }
  .device { font-family: 'Courier New', monospace; font-size: 1.5rem; font-weight: 700;
            color: var(--accent-deep); }
  @media (prefers-color-scheme: dark) { .device { color: var(--accent); } }
  .counter { background: var(--counter-bg); border-radius: 10px; padding: 14px 16px; text-align: center; }
  .counter .num { font-family: 'Courier New', monospace; font-size: 2.2rem; font-weight: 700; color: var(--counter-num); }
  .counter .cl { color: var(--counter-label); font-size: 11px; text-transform: uppercase; letter-spacing: 1.5px; }
  .track { width: 100%; height: 10px; border-radius: 6px; background: var(--track); overflow: hidden; margin-top: 12px; }
  #bar { height: 10px; width: 0; background: linear-gradient(90deg, #6b9039, #9bc466); transition: width .25s; }
  button { background: var(--btn); color: #f5ecd9; border: 0; border-radius: 8px;
           padding: 12px 26px; font-weight: 800; font-size: 1rem; letter-spacing: .5px; cursor: pointer;
           box-shadow: 0 3px 10px rgba(74,49,16,0.18); transition: .15s; }
  button:hover:not(:disabled) { background: var(--btn-hover); box-shadow: 0 5px 14px rgba(74,49,16,0.28); }
  button:disabled { background: #8b7d5e; opacity: .55; cursor: not-allowed; box-shadow: none; }
  pre { background: rgba(0,0,0,0.05); border-radius: 8px; padding: 10px; max-height: 150px;
        overflow: auto; font-size: .78rem; color: var(--text-soft); white-space: pre-wrap; margin: 0;
        font-family: 'Courier New', monospace; }
  @media (prefers-color-scheme: dark) { pre { background: rgba(0,0,0,0.25); } }
  .note { color: var(--text-soft); font-size: .82rem; }
  .slbl { display: flex; justify-content: space-between; font-size: .92rem; margin: 10px 0 4px; font-weight: 600; }
  .sval { font-family: 'Courier New', monospace; color: var(--accent); font-weight: 700; }
  input[type=range] { width: 100%; accent-color: var(--accent); }
  .note b { color: var(--warn); }
  .diff { color: var(--accent-deep); font-weight: 600; font-size: .88rem; }
  @media (prefers-color-scheme: dark) { .diff { color: var(--accent); } }
</style>
</head>
<body>
  <h1>🌼 DaisyChain-Web</h1>
  <p class="sub">Open this on your other devices <b>on the same network</b> and they <b>pretrain a language model from scratch</b> together — peer-to-peer, right in the browser, through the emulated GPU logic. This is pretraining, not fine-tuning: every run starts from random weights. Only devices on your network are grouped (like Snapdrop). To invite people across networks, everyone opens <code>?room=YOUR-CODE</code> — the person who created the room approves each device before it can join.</p>

  <div class="card" id="lobby" style="display:none;text-align:center">
    <div class="lbl">🏡 Get started</div>
    <p class="note" style="margin:0 0 12px">Create a room and invite your devices, or join a room someone shared with you.</p>
    <button id="createRoom">Create a room</button>
    <div style="display:flex;gap:8px;justify-content:center;margin-top:12px;flex-wrap:wrap">
      <input type="text" id="joinCode" placeholder="room code" spellcheck="false"
             style="padding:10px 12px;border-radius:8px;border:1px solid var(--card-border);background:transparent;color:inherit;font-family:'Courier New',monospace">
      <button id="joinRoom" style="padding:10px 18px">Join</button>
    </div>
  </div>

  <div class="card">
    <div class="lbl">🌲 This device</div>
    <div class="device" id="me"></div>
    <div class="row" style="margin-top:8px"><span class="k">Status</span><span class="v" id="status">starting…</span></div>
    <div class="row"><span class="k">Compute</span><span class="v" id="backend">detecting…</span></div>
    <div class="row"><span class="k">Dataset</span><span class="v" id="dataset"></span></div>
    <div class="row"><span class="k">Tokenizer</span><span class="v" id="tokenizer">loading…</span></div>
  </div>

  <div class="card">
    <div class="lbl">🍄 Devices in your group</div>
    <div class="row"><span class="v" id="peers" style="text-align:left">(none yet)</span></div>
    <div id="roomInfo" style="display:none;margin-top:8px">
      <div class="row"><span class="k">Room</span><span class="v" id="roomCode"></span></div>
      <div style="text-align:center;margin-top:6px"><button id="copyLink" style="padding:6px 14px;font-size:.85rem">Copy invite link</button></div>
      <p class="note" style="margin:.5rem 0 0;text-align:center">Open the link on your other devices — you approve each one before it joins.</p>
    </div>
    <div id="requests"></div>
  </div>

  <div class="card">
    <div class="lbl">🎛 Training settings</div>
    <label class="slbl">Model width <span class="sval" id="vcfgC">32</span></label>
    <input type="range" id="cfgC" min="16" max="128" step="16" value="32">
    <label class="slbl">Sequence length <span class="sval" id="vcfgT">32</span></label>
    <input type="range" id="cfgT" min="16" max="128" step="16" value="32">
    <p class="note" style="margin:.4rem 0 0"><b>Width or sequence above 64?</b> Bring more devices — big settings on 1–3 devices mean slow steps and a small effective batch. 4+ devices recommended.</p>
    <label class="slbl">Batch per device <span class="sval" id="vcfgB">8</span></label>
    <input type="range" id="cfgB" min="2" max="32" step="2" value="8">
    <label class="slbl">Steps <span class="sval" id="vcfgSteps">300</span></label>
    <input type="range" id="cfgSteps" min="100" max="10000" step="100" value="300">
    <label class="slbl">Learning rate ×1000 <span class="sval" id="vcfgLr">10</span></label>
    <input type="range" id="cfgLr" min="5" max="50" step="5" value="10">
    <label class="slbl">Dataset</label>
    <p class="note" style="margin:.2rem 0 0"><b>FineWeb-Edu</b> (HuggingFaceFW/fineweb-edu, 10BT sample) — fixed. Each device pulls its own random slice of the parquet shards, served by this Space; if streaming fails, the built-in corpus is used.</p>
    <p class="note" style="margin:.6rem 0 0"><b>This is pretraining, not fine-tuning</b> — every run trains a brand-new model from random weights. You are watching a language model learn from scratch.</p>
    <p class="note" style="margin:.5rem 0 0">A mini transformer language model — attention, MLP blocks, next-character prediction — every multiply through the verified INT8 units. Training text is streamed from <b>FineWeb-Edu</b> (HuggingFace); offline devices fall back to a built-in corpus. Whoever presses Start sets the settings for the whole group; total batch scales with every device that joins.</p>
  </div>

  <div class="card" style="text-align:center">
    <button id="start" disabled>Start training</button>
    <p class="note" style="margin:.6rem 0 0">Works solo — every device that joins adds its batch to the group.</p>
  </div>

  <div class="card">
    <div class="lbl">✦ Training</div>
    <div class="row"><span class="k">Step</span><span class="v" id="step">— / —</span></div>
    <div class="counter" style="margin:10px 0"><div class="num" id="loss"></div><div class="cl">cluster-avg loss · lower is better</div></div>
    <div class="track"><div id="bar"></div></div>
    <div class="row" style="margin-top:10px"><span class="diff" id="diff"></span></div>
  </div>

  <div class="card">
    <div class="lbl">🗣 Test the model</div>
    <div style="display:flex;gap:8px;flex-wrap:wrap">
      <input type="text" id="genPrompt" value="the " spellcheck="false"
             style="flex:1;min-width:160px;padding:10px 12px;border-radius:8px;border:1px solid var(--card-border);background:transparent;color:inherit;font-family:'Courier New',monospace">
      <button id="genBtn" disabled style="padding:10px 18px">Generate</button>
    </div>
    <pre id="genOut" style="margin-top:10px;min-height:44px"></pre>
    <p class="note" style="margin:.5rem 0 0">Enabled after training finishes or a checkpoint is loaded. The “inference kit” below downloads a single HTML file with these weights baked in — open it anywhere to run generations offline.</p>
  </div>

  <div class="card">
    <div class="lbl">💾 Model checkpoint</div>
    <div style="display:flex;gap:10px;flex-wrap:wrap;justify-content:center">
      <button id="save" disabled>Download model (.pt)</button>
      <button id="kit" disabled>Download inference kit</button>
      <button id="loadBtn">Load checkpoint…</button>
      <input type="file" id="load" accept=".pt" style="display:none">
    </div>
    <p class="note" style="margin:.6rem 0 0;text-align:center">Loading a checkpoint applies it here <b>and</b> pushes it to every connected device — use it to recover the group after a failure.</p>
  </div>

  <div class="card">
    <div class="lbl">❋ Log</div>
    <pre id="log"></pre>
  </div>

  <p class="note">Needs a secure context (localhost or HTTPS) for WebGPU + cross-device WebRTC. No WebGPU? The same verified INT8 units run on CPU — old machines (e.g. via Supermium) still join, just slower. Every training step goes through the Neural Units; there is no plain-float path, and if the units fail to load, training is disabled.</p>
  <p class="note"><b>Heads up:</b> peers connect directly (WebRTC), so devices in your group can see each other's IP address, and there's no gradient authentication — only train with devices/people you trust. Proof of concept.</p>

  <script src="traincore.js"></script>
  <script src="verified_core.js"></script>
  <script src="transformer.js"></script>
  <script src="webgpu.js"></script>
  <script src="app.js"></script>
</body>
</html>