beercan commited on
Commit
388c919
·
verified ·
1 Parent(s): e960200

Create mysterious.livemd

Browse files
Files changed (1) hide show
  1. public-apps/mysterious.livemd +492 -0
public-apps/mysterious.livemd ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- livebook:{"app_settings":{"access_type":"public","auto_shutdown_ms":5000,"multi_session":true,"output_type":"rich","show_existing_sessions":false,"slug":"mystery"},"autosave_interval_s":null,"file_entries":[{"file":{"file_system_id":"local","file_system_type":"local","path":"/data/messages.term"},"name":"messages.term","type":"file"}],"persist_outputs":true} -->
2
+
3
+ # Mysterious Crystal Ball
4
+
5
+ ```elixir
6
+ Mix.install(
7
+ [
8
+ {:bumblebee, "~> 0.5.3"},
9
+ {:nx, "~> 0.7.0"},
10
+ {:exla, "~> 0.7.0"},
11
+ {:axon, "~> 0.6.1"},
12
+ {:kino, "~> 0.12.0"},
13
+ {:req, "~> 0.4.11"},
14
+ {:kino_bumblebee, "~> 0.5.0"},
15
+ {:nimble_csv, "~> 1.2"}
16
+ ],
17
+ config: [nx: [default_backend: EXLA.Backend]]
18
+ )
19
+
20
+ Kino.Frame.new(placeholder: false)
21
+ ```
22
+
23
+ ## Section
24
+
25
+ ```elixir
26
+ html =
27
+ Kino.HTML.new("""
28
+ <style>
29
+
30
+ .truth-container {
31
+ display: flex;
32
+ flex-direction: column;
33
+ justify-content: center;
34
+ height: 38.5rem;
35
+ padding: 0.5rem;
36
+ }
37
+
38
+ .collapse-x {
39
+ transform: scaleX(0);
40
+ transition: transform 0.8s;
41
+ }
42
+
43
+ .collapse-y {
44
+ transform: scaley(0);
45
+ transition: transform 0.8s;
46
+ }
47
+
48
+ .gone {
49
+ display: none;
50
+ }
51
+
52
+ .exists {
53
+ display: block;
54
+ }
55
+
56
+ #beyond-truth {
57
+ width: 100%;
58
+
59
+ display: flex;
60
+ justify-content: center;
61
+ }
62
+
63
+ #beyond-truth.gone {
64
+ margin: 0;
65
+ padding: 0;
66
+ height: 0;
67
+ }
68
+
69
+ #globe-canvas {
70
+ margin: auto;
71
+ max-width: 100%;
72
+ width: 37.5rem;
73
+ height: auto;
74
+ }
75
+
76
+ #globe-canvas.gone {
77
+ height: 0;
78
+ }
79
+
80
+ .orb {
81
+ width: 40rem;
82
+ height: 40rem;
83
+ border-radius: 50%;
84
+ box-shadow: 0px 0px 16px rgba(81, 81, 231, 0.4); /* Subtle shadow */
85
+ }
86
+
87
+ .message {
88
+ margin-top: 20px;
89
+ font-family: monospace;
90
+ color: rgba(81, 81, 231, 1);
91
+ }
92
+
93
+ </style>
94
+ <div class="truth-container">
95
+ <canvas id="globe-canvas" width="600" height="600" class="orb"></canvas>
96
+ <div id="beyond-truth" class="message gone"></div >
97
+
98
+ </div>
99
+
100
+ <script>
101
+ const canvas = document.getElementById('globe-canvas');
102
+
103
+ canvas.addEventListener('click', shatterGlobe);
104
+ let isCollapsing = false;
105
+ let hasCollapsed = false;
106
+
107
+ const context = canvas.getContext('2d');
108
+
109
+ const tiltAngle = Math.PI / 228;
110
+ let rotation = 90;
111
+ const dots = [];
112
+ const radius = 120;
113
+
114
+ // Create dots on the globe
115
+ for (let lat = 0; lat < Math.PI; lat += Math.PI / 30) {
116
+ for (let lon = 0; lon < 2 * Math.PI; lon += Math.PI / 30) {
117
+ const x = radius * Math.sin(lat) * Math.cos(lon);
118
+ const y = radius * Math.sin(lat) * Math.sin(lon);
119
+ const z = radius * Math.cos(lat);
120
+ dots.push({x: x, y: y, z: z});
121
+ }
122
+ }
123
+
124
+ function draw() {
125
+
126
+ context.clearRect(0, 0, canvas.width, canvas.height);
127
+ context.translate(canvas.width / 2, canvas.height / 2);
128
+
129
+ context.rotate(tiltAngle);
130
+
131
+ // Rotate the globe
132
+ if (!hasCollapsed) {
133
+ rotation += 0.01;
134
+ }
135
+
136
+ dots.forEach(function(dot) {
137
+ const x = dot.x * Math.cos(rotation) - dot.z * Math.sin(rotation);
138
+ const y = dot.y;
139
+ const z = dot.z * Math.cos(rotation) + dot.x * Math.sin(rotation);
140
+
141
+ const brightness = 1 - (z / radius + 1.25) / 2;
142
+
143
+ const size = 2 - z / radius;
144
+ context.fillRect(x, y, size, size);
145
+ context.fillStyle = 'rgba(81, 81, 231, ' + brightness + ')';
146
+
147
+ });
148
+ context.rotate(-tiltAngle)
149
+
150
+ if (isCollapsing) {
151
+ let collapsed = true;
152
+ dots.forEach(dot => {
153
+ dot.y = (dot.y + 0.001 )* 1.015; // Adjust the speed of falling
154
+
155
+ // Check for bottom boundary
156
+ if (Math.abs(dot.y) > canvas.height / 2) {
157
+ dot.y = canvas.height / 2; // Stop it at the 'ground'
158
+ } else {
159
+ collapsed = false;
160
+ }
161
+ });
162
+
163
+ if (collapsed) {
164
+ hasCollapsed = true;
165
+ }
166
+ }
167
+
168
+ context.translate(-canvas.width / 2, -canvas.height / 2);
169
+
170
+ if (!hasCollapsed) {
171
+ requestAnimationFrame(draw);
172
+ } else {
173
+ console.log({why: "Look what you have done to my precious orb!"});
174
+ canvas.classList.add("collapse-x");
175
+ setTimeout(() => {
176
+ canvas.classList.add("collapse-y");
177
+ }, 1000)
178
+ setTimeout(() => {
179
+ canvas.classList.add("gone");
180
+ }, 2000)
181
+ setTimeout(() => {
182
+ const beyondTruth = document.getElementById("beyond-truth");
183
+ beyondTruth.classList.remove("gone");
184
+
185
+ const subTitle = document.createElement("h3");
186
+ subTitle.innerText = "Now what is the secret of Today?";
187
+ beyondTruth.appendChild(subTitle);
188
+ }, 2100)
189
+ }
190
+ }
191
+
192
+ function shatterGlobe() {
193
+ isCollapsing = true;
194
+ }
195
+ draw();
196
+ </script>
197
+
198
+ """)
199
+
200
+ main_frame = Kino.Frame.new() |> Kino.render()
201
+ Kino.Frame.render(main_frame, html)
202
+
203
+ Kino.Frame.new(placeholder: false)
204
+ ```
205
+
206
+ ```elixir
207
+ message_frame = Kino.Frame.new(placeholder: false) |> Kino.render()
208
+
209
+ message_pid =
210
+ spawn(fn ->
211
+ receive_message = fn loop ->
212
+ receive do
213
+ {:message, message} ->
214
+ Kino.Frame.render(
215
+ message_frame,
216
+ Kino.HTML.new("""
217
+ <div class="message" style="color: rgba(81, 81, 231, 0.8); display: flex;">
218
+ <pre style="margin: auto;">#{message}</pre>
219
+ </div>
220
+ """)
221
+ )
222
+
223
+ loop.(loop)
224
+
225
+ _ ->
226
+ {:error}
227
+ end
228
+ end
229
+
230
+ receive_message.(receive_message)
231
+ end)
232
+
233
+ Kino.Frame.new(placeholder: false)
234
+ ```
235
+
236
+ ```elixir
237
+ status_frame = Kino.Frame.new(placeholder: false) |> Kino.render()
238
+
239
+ status_pid =
240
+ spawn(fn ->
241
+ receive_messages = fn loop ->
242
+ receive do
243
+ {:status, status} ->
244
+ Kino.Frame.render(
245
+ status_frame,
246
+ Kino.HTML.new("""
247
+ <div style="position: absolue; bottom: 0px; font-size: 6px; color: rgba(81, 81, 231, 0.4)"><pre>Status: #{status}</pre></div>
248
+ """)
249
+ )
250
+
251
+ loop.(loop)
252
+
253
+ _ ->
254
+ {:error}
255
+ end
256
+ end
257
+
258
+ receive_messages.(receive_messages)
259
+ end)
260
+
261
+ Kino.Frame.new(placeholder: false)
262
+ ```
263
+
264
+ ```elixir
265
+ file_name = "messages.term"
266
+
267
+ case File.exists?(file_name) do
268
+ false -> File.touch!(file_name)
269
+ _ -> :ok
270
+ end
271
+
272
+ binary =
273
+ Kino.FS.file_path(file_name)
274
+ |> File.read!()
275
+
276
+ extract_today = fn binary ->
277
+ [result | _] = :erlang.binary_to_term(binary)
278
+
279
+ case result.date == Date.utc_today() do
280
+ true -> {:ok, result.data}
281
+ _ -> {:no, false}
282
+ end
283
+ end
284
+
285
+ loaded_today_data =
286
+ case String.length(binary) == 0 do
287
+ true -> {:no, false}
288
+ _ -> extract_today.(binary)
289
+ end
290
+
291
+ frame = Kino.Frame.new(placeholder: false)
292
+ ```
293
+
294
+ ```elixir
295
+ bumblebee = fn ->
296
+ {:ok, cosmo} = Bumblebee.load_model({:hf, "HuggingFaceTB/cosmo-1b"})
297
+ {:ok, tokenizer} = Bumblebee.load_tokenizer({:hf, "HuggingFaceTB/cosmo-1b"})
298
+ {:ok, generation_config} = Bumblebee.load_generation_config({:hf, "HuggingFaceTB/cosmo-1b"})
299
+
300
+ generation_config =
301
+ Bumblebee.configure(generation_config,
302
+ max_new_tokens: 50,
303
+ strategy: %{type: :multinomial_sampling, top_p: 0.6}
304
+ )
305
+
306
+ serving =
307
+ Bumblebee.Text.generation(cosmo, tokenizer, generation_config,
308
+ compile: [batch_size: 1, sequence_length: 256],
309
+ stream: true,
310
+ defn_options: [compiler: EXLA]
311
+ )
312
+
313
+ Kino.start_child!({Nx.Serving, name: :cosmo, serving: serving})
314
+ %{name: :cosmo, serving: serving}
315
+ end
316
+
317
+ crypticize = fn subject, i ->
318
+ prompt =
319
+ "<s> [INST]You will write a cryptic message in the form of a haiku in paragraph form about this headline: \"#{subject}\"[/INST]"
320
+
321
+ tokens =
322
+ Nx.Serving.batched_run(:cosmo, prompt)
323
+ |> Stream.chunk_every(2)
324
+ |> Enum.reduce([], fn token, acc -> acc ++ token end)
325
+
326
+ send(status_pid, {:status, i})
327
+
328
+ tokens
329
+ |> Enum.join()
330
+ end
331
+
332
+ Kino.Frame.new(placeholder: false)
333
+ ```
334
+
335
+ ```elixir
336
+ themes = [
337
+ "business",
338
+ "technology",
339
+ "science",
340
+ "health",
341
+ "politics",
342
+ "world",
343
+ "education",
344
+ "entertainment",
345
+ "games",
346
+ "movie",
347
+ "TV",
348
+ "lifestyle",
349
+ "opinion",
350
+ "self-improvement",
351
+ "positivism",
352
+ "fun%20facts",
353
+ "community",
354
+ "wholesome",
355
+ "human%20interest",
356
+ "inspirational"
357
+ ]
358
+
359
+ theme = themes |> Enum.random()
360
+
361
+ select_message = fn messages ->
362
+ sanitize = fn text ->
363
+ text
364
+ |> String.replace(~r/\[\/INST\]/, "")
365
+ |> String.replace(~r/\[INST\]/, "")
366
+ end
367
+
368
+ messages |> Enum.random() |> sanitize.()
369
+ end
370
+
371
+ news_loader = fn ->
372
+ response =
373
+ receive do
374
+ {:fetched, data} -> data
375
+ _ -> raise "Could not fetch data"
376
+ end
377
+
378
+ bumblebee.()
379
+
380
+ results = response.body["results"]
381
+
382
+ messages =
383
+ results
384
+ |> Enum.map(fn news_item -> news_item["title"] end)
385
+ |> Enum.with_index()
386
+ |> Enum.map(fn {subject, index} -> crypticize.(subject, index) end)
387
+
388
+ date = Date.utc_today()
389
+
390
+ data =
391
+ messages
392
+ |> Enum.join("{nextItem}")
393
+
394
+ map = %{date: date, data: data, theme: theme}
395
+
396
+ prev_data =
397
+ case String.length(binary) == 0 do
398
+ true -> []
399
+ _ -> :erlang.binary_to_term(binary)
400
+ end
401
+
402
+ [map] ++ prev_data
403
+ end
404
+
405
+ handler_pid =
406
+ case loaded_today_data do
407
+ {:no, _} ->
408
+ spawn(fn ->
409
+ receive do
410
+ {:fetched, data} -> File.write(file_name, :erlang.term_to_binary(data))
411
+ _ -> raise "Could not analyze data"
412
+ end
413
+ end)
414
+
415
+ _ ->
416
+ false
417
+ end
418
+
419
+ loader_pid =
420
+ case loaded_today_data do
421
+ {:no, _} ->
422
+ spawn(fn ->
423
+ data = news_loader.()
424
+
425
+ if is_pid(handler_pid) do
426
+ send(handler_pid, {:fetched, data})
427
+ end
428
+
429
+ [first_data | _] = data
430
+
431
+ messages =
432
+ first_data.data
433
+ |> String.split("{nextItem}")
434
+
435
+ Process.send_after(message_pid, {:message, select_message.(messages)}, 500)
436
+ end)
437
+
438
+ _ ->
439
+ false
440
+ end
441
+
442
+ fetcher_pid =
443
+ case loaded_today_data do
444
+ {:no, _} ->
445
+ spawn(fn ->
446
+ apikey = System.fetch_env!("LB_NEWSDATA_KEY")
447
+ data = Req.get!("https://newsdata.io/api/1/news?apikey=#{apikey}&q=#{theme}")
448
+
449
+ if is_pid(loader_pid) do
450
+ send(loader_pid, {:fetched, data})
451
+ end
452
+ end)
453
+
454
+ _ ->
455
+ false
456
+ end
457
+
458
+ get_first = fn binary ->
459
+ [head | _tail] = :erlang.binary_to_term(binary)
460
+ head
461
+ end
462
+
463
+ unpack_data = fn data ->
464
+ {:ok, unpacked} = data
465
+ unpacked
466
+ end
467
+
468
+ today_data =
469
+ case loaded_today_data do
470
+ {:no, _} ->
471
+ first = get_first.(binary)
472
+ first.data
473
+
474
+ _ ->
475
+ unpack_data.(loaded_today_data)
476
+ end
477
+
478
+ Kino.Frame.new(placeholder: false)
479
+ ```
480
+
481
+ ```elixir
482
+ random_message =
483
+ today_data
484
+ |> String.split("{nextItem}")
485
+ |> select_message.()
486
+
487
+ send(message_pid, {:message, random_message})
488
+
489
+ Kino.Frame.new(placeholder: false)
490
+ ```
491
+
492
+ <!-- livebook:{"offset":11741,"stamp":{"token":"XCP.vc3mSQeIkJ_U7bagwkvIUj0CSd_xYPlg90_EKSaJdabfZXO3hHUC7y3mKwe-siZE75IRQbiEnP83fb1pV3yqAyYu-WvRjroWPp_8zP4BXMFEEbM_ldiT21ED3Pe9zFTfSKsHs6uxnEPfSGs7zflaGO4chw-V","version":2}} -->