Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions static/frp-graph-04/apiKey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function fetchApiKey() {
let apiKey = localStorage.getItem("apiKey");

if (!apiKey) {
// Prompt the user for the API key if it doesn't exist
const userApiKey = prompt("Please enter your API key:");

if (userApiKey) {
// Save the API key in localStorage
localStorage.setItem("apiKey", userApiKey);
apiKey = userApiKey;
} else {
// Handle the case when the user cancels or doesn't provide an API key
alert("API key not provided. Some features may not work.");
}
}

return apiKey;
}
22 changes: 22 additions & 0 deletions static/frp-graph-04/connect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
distinctUntilChanged,
share,
tap,
Subject,
} from "https://cdn.jsdelivr.net/npm/rxjs@7.8.1/+esm";
import { applyPolicy } from "./policy.js";

export function connect(output, input) {
output
.pipe(
distinctUntilChanged(),
applyPolicy(),
tap((v) => input.next(v)),
share(),
)
.subscribe();
}

export function ground(output) {
connect(output, new Subject());
}
79 changes: 79 additions & 0 deletions static/frp-graph-04/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const name$ = BehaviourNode("");
const nameUi$ = GeneratedNameUI();
const nameTagUi$ = GeneratedNameTagUI();
const danger$ = DangerousUI();
const cursor$ = Cursor();
const combined$ = CombinedDataUI();
const backstoryUi$ = GeneratedBackstoryUI();
const portraitUi$ = PortraitUI();
const statsUi$ = GeneratedStatsUI();

const character$ = combineLatest([
nameUi$.out.name,
statsUi$.out.str,
statsUi$.out.dex,
statsUi$.out.con,
statsUi$.out.int,
statsUi$.out.wis,
statsUi$.out.cha,
]);

const backstory$ = character$.pipe(
filter(
(v) =>
v.name.length > 0 &&
(v.stats.str > 0 ||
v.stats.dex > 0 ||
v.stats.con > 0 ||
v.stats.int > 0 ||
v.stats.wis > 0 ||
v.stats.cha > 0),
),
debounceTime(1000),
distinctUntilChanged(),
switchMap((character) => {
console.log("character", character);
return from(
doLLM(
JSON.stringify(character),
"Write a possible backstory for this fantasy character in 280 characters or less.",
),
);
}),
map(extractResponse),
share(),
);

const image$ = backstory$.pipe(
debounceTime(1000),
distinctUntilChanged(),
switchMap((backstory) => {
console.log("backstory", backstory);
return from(
generateImage(
"Create a fantasy portrait of character based on this bio: " +
backstory,
),
);
}),
share(),
);

connect(name$.out.value, nameUi$.in.name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔗


connect(nameUi$.out.name, nameTagUi$.in.name);
connect(nameUi$.out.name, nameTagUi$.in.render);
connect(backstory$, backstoryUi$.in.backstory);
connect(backstory$, backstoryUi$.in.render);
connect(image$, portraitUi$.in.img);
connect(image$, portraitUi$.in.render);

connect(character$, combined$.in.data);
connect(character$, combined$.in.render);

ground(nameUi$.out.ui);
ground(nameTagUi$.out.ui);
ground(combined$.out.ui);
ground(backstoryUi$.out.ui);
ground(portraitUi$.out.ui);
ground(statsUi$.out.ui);
138 changes: 138 additions & 0 deletions static/frp-graph-04/graph.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {
combineLatest,
debounceTime,
delay,
distinctUntilChanged,
filter,
from,
fromEvent,
map,
mergeMap,
share,
switchMap,
tap,
} from "https://cdn.jsdelivr.net/npm/rxjs@7.8.1/+esm";
import { connect, ground } from "./connect.js";
import { doLLM, extractResponse, generateImage } from "./llm.js";
import { BehaviourNode } from "./nodes/BehaviourNode.js";
import { CombinedDataUI } from "./nodes/CombinedDataUI.js";
import { Cursor } from "./nodes/Cursor.js";
import { DangerousUI } from "./nodes/DangerousUI.js";
import { GeneratedBackstoryUI } from "./nodes/GeneratedBackstoryUI.js";
import { GeneratedNameTagUI } from "./nodes/GeneratedNameTagUI.js";
import { GeneratedNameUI } from "./nodes/GeneratedNameUI.js";
import { PortraitUI } from "./nodes/PortraitUI.js";
import { GeneratedStatsUI } from "./nodes/GeneratedStatsUI.js";

const startButton = document.getElementById("startWorkflow");

const name$ = BehaviourNode("");
const nameUi$ = GeneratedNameUI();
const nameTagUi$ = GeneratedNameTagUI();
const danger$ = DangerousUI();
const cursor$ = Cursor();
const combined$ = CombinedDataUI();
const backstoryUi$ = GeneratedBackstoryUI();
const portraitUi$ = PortraitUI();
const statsUi$ = GeneratedStatsUI();

const character$ = combineLatest([
nameUi$.out.name,
statsUi$.out.str,
statsUi$.out.dex,
statsUi$.out.con,
statsUi$.out.int,
statsUi$.out.wis,
statsUi$.out.cha,
]).pipe(
map(([name, str, dex, con, int, wis, cha]) => ({
name,
stats: {
str,
dex,
con,
int,
wis,
cha,
},
})),
);

const backstory$ = character$.pipe(
filter(
(v) =>
v.name.length > 0 &&
(v.stats.str > 0 ||
v.stats.dex > 0 ||
v.stats.con > 0 ||
v.stats.int > 0 ||
v.stats.wis > 0 ||
v.stats.cha > 0),
),
debounceTime(1000),
distinctUntilChanged(),
switchMap((character) => {
console.log("character", character);
return from(
doLLM(
JSON.stringify(character),
"Write a possible backstory for this fantasy character in 280 characters or less.",
),
);
}),
map(extractResponse),
share(),
);

const image$ = backstory$.pipe(
debounceTime(1000),
distinctUntilChanged(),
switchMap((backstory) => {
console.log("backstory", backstory);
return from(
generateImage(
"Create a fantasy portrait of character based on this bio: " +
backstory,
),
);
}),
share(),
);

connect(name$.out.value, nameUi$.in.name);

connect(character$, combined$.in.data);
connect(character$, combined$.in.render);

connect(nameUi$.out.name, nameTagUi$.in.name);
connect(nameUi$.out.name, nameTagUi$.in.render);
connect(backstory$, backstoryUi$.in.backstory);
connect(backstory$, backstoryUi$.in.render);
connect(image$, portraitUi$.in.img);
connect(image$, portraitUi$.in.render);

ground(nameUi$.out.ui);
ground(nameTagUi$.out.ui);
ground(combined$.out.ui);
// ground(danger$.out.ui);
ground(backstoryUi$.out.ui);
ground(portraitUi$.out.ui);
ground(statsUi$.out.ui);

character$.subscribe(console.log);

ground(
fromEvent(startButton, "click").pipe(
tap(() => {
// name$.in.value.next("Ben" + Math.floor(Math.random() * 1000));
nameUi$.in.generate.next();
nameTagUi$.in.generate.next();
// danger$.in.generate.next();
backstoryUi$.in.generate.next();
statsUi$.in.generate.next();

cursor$.in.render.next();
combined$.in.render.next();
}),
),
);
31 changes: 31 additions & 0 deletions static/frp-graph-04/imagine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
mergeMap,
map,
from,
tap,
} from "https://cdn.jsdelivr.net/npm/rxjs@7.8.1/+esm";
import { doLLM, extractResponse, grabViewTemplate, uiPrompt } from "./llm.js";
import { applyPolicy } from "./policy.js";
import { render } from "./render.js";

export function placeholder(id) {
return tap((description) => {
render(id, `<div class="description">{{description}}</div>`, {
description,
});
});
}

export function imagine(id, prompt) {
return (v) =>
v.pipe(
map(() => prompt),
placeholder(id),
mergeMap((description) =>
from(doLLM(description + "Return only the code.", uiPrompt)),
),
map(extractResponse),
map(grabViewTemplate),
applyPolicy(),
);
}
56 changes: 56 additions & 0 deletions static/frp-graph-04/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!doctype html>
<html>
<head>
<title>rxjs</title>
</head>
<style type="text/css">
.debug {
font-size: 8px;
max-height: 128px;
max-width: 50vw;
overflow-y: auto;
background-color: #f0f0f0;
border: 1px solid #ccc;
padding: 4px;
border-radius: 4px;
}

#workflow {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 8px;
}

#workflow > * {
border: 1px dashed #ccc;
padding: 8px;
}

.columns {
display: flex;
gap: 16px;
}

.columns > * {
flex: 1;
}

.description {
font-size: 10px;
font-family: monospace;
}
</style>
<body>
<button id="initialRender">Initial Render</button>
<button id="startWorkflow">Start Workflow</button>
<div class="columns">
<div id="workflow"></div>
<div id="debug"></div>
</div>

<script type="module">
import "./graph.js";
</script>
</body>
</html>
Loading