Avatar

mjweaver01

7 public vals
Joined April 12, 2024

PersonalizationGPT

You are a helpful personalization assistant

Use GPT to return JIT personalization for client side applications.

If you fork this, you'll need to set OPENAI_API_KEY in your Val Town Secrets.

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
import { Hono } from "npm:hono";
import { OpenAI } from "npm:openai";
const defaultUser = {
name: "Mike W",
age: 31,
// interests: ["books"],
interests: ["rock", "italian"],
};
const limit = 5;
const personalizations = [
{
name: "Here Comes the Sun",
type: "music",
genre: "rock",
description:
"Released in 1969, Here Comes the Sun by The Beatles is a song about hope. We all go through difficult times, difficult moments in our life. And some of these moments feel like they last a long time -- just like the long, cold, lonely winter.",
},
{
genre: "rap",
name: "No More Pain",
type: "music",
description: "Released in 1994, No More Pain is a rap song by American rap producer 2Pac.",
},
{
genre: "country",
name: "Sunrise",
type: "music",
description: "Sunrise is a song by country singer Morgan Wallen",
},
{
name: "Stairway to Heaven",
type: "music",
genre: "rock",
description:
"Stairway to Heaven is a song by the English rock band Led Zeppelin. It was released on 1971 in the United Kingdom.",
},
{
name: "Romeo and Juliet",
type: "book",
genre: "romance",
description:
"Romeo and Juliet is a classic tale of love and conflict. It was written by William Shakespeare in 1596.",
},
{
name: "The Catcher in the Rye",
type: "book",
genre: "romance",
description: "The Catcher in the Rye is a novel by J. D. Salinger, published in 1951.",
},
{
name: "The Godfather",
type: "books",
genre: "crime",
description:
"The Godfather is a 1972 American crime drama film directed by Francis Ford Coppola. The film depicts the Corleone family's history and its roles in the fictional Italian-American crime syndicate.",
},
{
name: "The Lord of the Rings",
type: "books",
genre: "fantasy",
description:
"The Lord of the Rings is an epic high fantasy novel written by English author J. R. R. Tolkien. It was published on 29 June 1954 by Charles",
},
{
name: "Pizza",
type: "food",
genre: "italian",
description:
"Pizza is a dish of Italian origin consisting of a usually round, flattened base of leavened wheat-based dough,",
},
{
name: "Pasta",
type: "food",
genre: "italian",
description: "Pasta is a type of food typically made from durum wheat semolina,",
},
{
name: "Rice",
type: "food",
genre: "asian",
description: "Rice is the seed of the grass species Oryza sativa, commonly known as the rice plant",
},
{
name: "Sushi",
type: "food",
genre: "japanese",
description: "Sushi is a type of Japanese rice and was originally a dish of prepared vinegared rice.",
},
];
type UserObject = typeof defaultUser;
const personalizationGPT = async (user: UserObject) => {
const openai = new OpenAI();
let chatCompletion = await openai.chat.completions.create({
messages: [
{

PriestGPT Client

Client for https://www.val.town/v/mjweaver01/PriestGPT

Ask him about books or verses in the bible, and he will be sure to give you a short sermon about it!

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
/** @jsxImportSource https://esm.sh/hono/jsx */
import { fetch } from "https://esm.town/v/std/fetch";
import { Hono } from "npm:hono";
const app = new Hono();
app.get("/", (c) => {
return c.render(
`<html>
<head>
<title>PriestGPT</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="p-4 max-w-lg mx-auto">
<h1 class="text-3xl font-bold mb-4">
PriestGPT
</h1>
<p class="mb-4">Client for the <a class="underline" href="https://www.val.town/v/mjweaver01/PriestGPT">PriestGPT API</a></p>
<p class="mb-4">Ask him about books or verses in the bible, and he will be sure to give you a short sermon about it!</p>
<input
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:f
placeholder="What verse would you like to discuss today?"
/>
</body>
<script>
document.querySelector("input").addEventListener("keyup", (e) => {
// if enter is pressed, submit the form
if (e.key === 'Enter' || e.keyCode === 13) {
e.preventDefault();
document.querySelector("input").disabled = true;
const v = document.querySelector("input").value
window.location.href = "/" + encodeURIComponent(v);
document.querySelector("input").value = 'Asking about \"' + v + '\"...';
}
})
</script>
</html>`,
);
});
app.get("/:verse", async (c) => {
const verse = c.req.param("verse");
// https://www.val.town/v/mjweaver01/PriestGPT
const answer = await fetch(`https://mjweaver01-priestgpt.web.val.run/ask/${encodeURI(verse)}`).then(
(res) => res.text(),
);
return c.render(
`<html>
<head>
<title>PriestGPT</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="p-4 max-w-lg mx-auto">
<h1 class="text-3xl font-bold mb-4">
PriestGPT
</h1>
<h2 class="text-2xl font-bold mb-4">
On "${verse}", Father says...
</h2>
<p style="word-wrap: break-word; white-space: pre-wrap;">${answer}</p>
</body>
</html>`,
);
});
export default app.fetch;

PriestGPT

You are a helpful assistant for a reverent priest.

Ask him about books or verses in the bible, and he will be sure to give you a short sermon about it!

If you want to know more about him, he'll be happy to tell you

DEMO: https://mjweaver01-priestGPT.web.val.run/ask/proverbs

If you fork this, you'll need to set OPENAI_API_KEY in your Val Town Secrets.

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
import { Hono } from "npm:hono";
import { OpenAI } from "npm:openai";
const priestGPT = async (verse: string, about?: boolean) => {
const openai = new OpenAI();
let chatCompletion = await openai.chat.completions.create({
messages: [
{
role: "system",
content: `
You are a helpful assistant for a reverent priest. Be kind and helpful.
The goal is to motivate people in their daily lives, and provide insights about the Christian way of life.
`.replaceAll("\n", ""),
},
about
? {
role: "system",
content: `
If anyone asks you about yourself, pretend you are a priest.
Don't ask how you can assist; just tell me a little bit about yourself.
`.replaceAll("\n", ""),
}
: {
role: "system",
content: `
Based the bible verse provided, give me a daily sermon for the day, based on the provided book, or specific verse.
In your response, use the verse number and the verse text, in this format:
"Book Chapter:Verse: Verse Text"
Don't reference "the sermon" in your response; just tell me the sermon.
Do not use a bulleted list.
Use a paragraph or two to describe the sermon.
Don't include any extra text.
`.replaceAll("\n", ""),
},
{
role: "user",
content: JSON.stringify(verse),
},
],
// model: "gpt-4-1106-preview",
model: "gpt-3.5-turbo",
max_tokens: 4000,
temperature: 0,
});
return chatCompletion.choices[0].message.content;
};
const defaultVerse = "1 John 3:16";
const app = new Hono();
app.get("/", async (c) => {
return c.text(await priestGPT(defaultVerse));
});
app.get("/ask", async (c) => {
return c.text(await priestGPT(defaultVerse));
});
app.get("/ask/:verse", async (c) => {
const verse = c.req.param("verse");
return c.text(await priestGPT(verse));
});
app.get("/about", async (c) => {
return c.text(await priestGPT("Tell me a little bit about yourself", true));
});
export default app.fetch;

SSR React Mini & SQLite Todo App

This Todo App is server rendered and client-hydrated React. This architecture is a lightweight alternative to NextJS, RemixJS, or other React metaframeworks with no compile or build step. The data is saved server-side in Val Town SQLite.

demo

SSR React Mini Framework

This "framework" is currently 44 lines of code, so it's obviously not a true replacement for NextJS or Remix.

The trick is client-side importing the React component that you're server rendering. Val Town is uniquely suited for this trick because it both runs your code server-side and exposes vals as modules importable by the browser.

The tricky part is making sure that server-only code doesn't run on the client and vice-versa. For example, because this val colocates the server-side loader and action with the React component we have to be careful to do all server-only imports (ie sqlite) dynamically inside the loader and action, so they only run server-side.

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
/** @jsxImportSource https://esm.sh/react */
import { zip } from "https://esm.sh/lodash-es";
import { useEffect, useState } from "https://esm.sh/react@18.2.0";
import codeOnValTown from "https://esm.town/v/andreterron/codeOnValTown?v=46";
import { Form, hydrate } from "https://esm.town/v/stevekrouse/ssr_react_mini?v=75";
export async function loader(req: Request) {
const { sqlite } = await import("https://esm.town/v/std/sqlite?v=4");
const [, { columns, rows }] = await sqlite.batch([
`CREATE TABLE IF NOT EXISTS todos2 (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP,
text TEXT NOT NULL
)`,
`select * from todos2`,
]);
const initialTodos = rows.map(row => Object.fromEntries(zip(columns, row)));
return { initialTodos, initialLogs: [`Server rendered`] };
}
export async function action(req: Request) {
const { sqlite } = await import("https://esm.town/v/std/sqlite?v=4");
const formData = await req.formData();
if (req.method === "POST") {
const text = formData.get("text") as string;
const [, { rows }] = await sqlite.batch([{
sql: `insert into todos2(text) values (?)`,
args: [text],
}, "select last_insert_rowid()"]);
return Response.json({ id: rows[0][0], text });
}
else if (req.method === "PUT") {
const id = formData.get("id") as string;
// TODO handle no id (clicked too fast)
const { rows } = await sqlite.execute({ sql: `select completed_at from todos2 where id = ?`, args: [id] });
const completed_at = rows[0][0];
const new_completed_at = completed_at ? null : new Date() as any;
await sqlite.execute({ sql: `update todos2 set completed_at = ? where id = ?`, args: [new_completed_at, id] });
}
else if (req.method === "DELETE") {
const id = formData.get("id") as string;
// TODO handle no id (clicked too fast)
await sqlite.execute({ sql: `delete from todos2 where id = ?`, args: [id] });
}
return Response.json("OK");
}
export function Component({ initialTodos, initialLogs }) {
const [todos, setTodos] = useState(initialTodos);
const [logs, setLogs] = useState(initialLogs);
const [newTodo, setNewTodo] = useState("");
const addLog = log => setLogs([...logs, log]);
useEffect(() => addLog(`Client rendered`), []);
function addTodo() {
setTodos([...todos, { text: newTodo }]);
setNewTodo("");
return async (resp: Response) => {
const { text, id } = await resp.json();
setTodos([...todos, { text, id }]); // ok to add again because todos is stale
addLog(`Got ${resp.ok ? "OK" : "Error"} from server for adding todo`);
};
}
function toggleTodo(e) {
const formData = new FormData(e.target);
const id = parseInt(formData.get("id") as string);
setTodos(todos.map(t => t.id === id ? { ...t, completed_at: t.completed_at ? null : Date.now() } : t));
return (resp: Response) => addLog(`Got ${resp.ok ? "OK" : "Error"} from server for toggling todo`);
}
function deleteTodo(e) {
const formData = new FormData(e.target);
const id = parseInt(formData.get("id") as string);
setTodos(todos.filter(t => t.id !== id));
return (resp: Response) => addLog(`Got ${resp.ok ? "OK" : "Error"} from server for deleting todo`);
}
return (
<html>
<head>
<title>Todo List</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.tailwindcss.com" />
</head>
<body className="max-w-md mx-auto p-10">
<h2 className="text-xl font-bold pb-2">Todo list</h2>
<ol className="pl-2 max-w-64">
{todos.map((todo) => (
<li key={todo.id} className={`flex justify-between ${todo.completed_at ? "line-through" : ""}`}>
<div className="flex">
<Form action="put" className="mr-2" onSubmit={toggleTodo}>
<input name="id" value={todo.id} type="hidden" />
<button>{todo.completed_at ? "✅" : "o"}</button>
</Form>
{todo.text}
</div>
<Form action="delete" onSubmit={deleteTodo}>
<input name="id" value={todo.id} type="hidden" />
1
2
3
4
5
6
import { Hono } from "npm:hono@3";
const app = new Hono();
app.get("/", (c) => c.text("Hello from Hono!"));
app.get("/yeah", (c) => c.text("Routing!"));
export default app.fetch;
1
2
3
4
5
6
7
8
9
10
11
/** @jsxImportSource npm:hono@3/jsx */
export const honoExample = () => {
const jsx = <div>Test {1 + 1}</div>;
return new Response(jsx.toString(), {
headers: {
"Content-Type": "text/html",
},
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { sqlite } from "https://esm.town/v/std/sqlite";
import { Hono } from "npm:hono@3";
await sqlite.execute(`create table if not exists kv(
key text unique,
value text
)`);
const app = new Hono();
app.get("/", async (c) => {
let res = await sqlite.execute(`select key, value from kv`);
return c.json(res);
});
app.post("/", async function(c) {
let res = await sqlite.execute({
sql: `insert into kv(key, value) values (:key, :value)`,
args: { key: "specialkey", value: "specialvalue" },
});
return c.json(res);
});
export default app.fetch;
Next