Skip to content

Instantly share code, notes, and snippets.

@galopyz
Created August 6, 2025 16:39
Show Gist options
  • Select an option

  • Save galopyz/758d52efbb3f8f80a56abf0a1d188c32 to your computer and use it in GitHub Desktop.

Select an option

Save galopyz/758d52efbb3f8f80a56abf0a1d188c32 to your computer and use it in GitHub Desktop.
My Dialog
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "c635be77",
"metadata": {
"input_tokens": 9
},
"source": [
"# TODO app in Solveit"
]
},
{
"cell_type": "markdown",
"id": "2c1758a0",
"metadata": {
"input_tokens": 72
},
"source": [
"We will build a simple todo app in solveit. The example we will use is [todo4.py](https://github.com/AnswerDotAI/fasthtml/blob/main/examples/todos4.py) from fasthtml examples.\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "fdf35de0",
"metadata": {
"input_tokens": 85
},
"source": [
"To run a FastHTML example code in solveit, we have to make some adjustments.\n",
"\n",
"- Change routes from `/` to something else because solveit uses `/`. We will use `/hp`.\n",
"- Only GET and POST methods work. To DELETE or PUT, we will use POST."
]
},
{
"cell_type": "markdown",
"id": "75a31f93",
"metadata": {
"input_tokens": 34
},
"source": [
"We first import fasthtml and monsterui. Then we also import dialoghelper because we are in solveit.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4b63932f",
"metadata": {
"input_tokens": 51,
"time_run": "4:26:14p"
},
"outputs": [],
"source": [
"from fasthtml.common import *\n",
"from fasthtml.jupyter import *\n",
"from monsterui.all import *\n",
"\n",
"from dialoghelper import *\n",
"from contextkit import read_gh_file"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a2bce162",
"metadata": {
"input_tokens": 31
},
"outputs": [],
"source": [
"# Run once to create a note cell containing all the tools available in dialoghelper\r\n",
"# tool_info()"
]
},
{
"cell_type": "markdown",
"id": "8428c717",
"metadata": {
"input_tokens": 270
},
"source": [
"Tools available from `dialoghelper`:\n",
"\n",
"- &`curr_dialog`: Get the current dialog info.\n",
"- &`msg_idx`: Get absolute index of message in dialog.\n",
"- &`add_html`: Send HTML to the browser to be swapped into the DOM using hx-swap-oob.\n",
"- &`find_msg_id`: Get the current message id.\n",
"- &`find_msgs`: Find messages in current specific dialog that contain the given information.\n",
" - (solveit can often get this id directly from its context, and will not need to use this if the required information is already available to it.)\n",
"- &`read_msg`: Get the message indexed in the current dialog.\n",
"- &`del_msg`: Delete a message from the dialog.\n",
"- &`add_msg`: Add/update a message to the queue to show after code execution completes.\n",
"- &`update_msg`: Update an existing message."
]
},
{
"cell_type": "markdown",
"id": "b6a2abc2",
"metadata": {
"input_tokens": 13
},
"source": [
"Let's grab an example we want to use."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e3bc79cd",
"metadata": {
"input_tokens": 49,
"output_tokens": 84,
"time_run": "4:26:18p"
},
"outputs": [
{
"data": {
"text/plain": [
"\"from fasthtml.common import *\\n\\ndb = database('data/todos.db')\\ntodos = db.t.todos\\nif todos not in db.\""
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gh_file = read_gh_file('https://github.com/AnswerDotAI/fasthtml/blob/main/examples/todos4.py')\r\n",
"gh_file[:100]"
]
},
{
"cell_type": "markdown",
"id": "4dc42598",
"metadata": {
"input_tokens": 138
},
"source": [
"Using a prompt like `Can you split the code in $gh_file into smaller chunks with one function definition in each code cell? Also separate variable declrations or library imports. Use the tools from dialoghelper.`, we can use dialoghelper to cut the python script into smaller pieces. It may add the code cells in reverse order. There is also a limit on how many loops we can use in a tool loop, so it may not finish splitting."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8adc4503",
"metadata": {
"input_tokens": 66,
"time_run": "4:26:20p"
},
"outputs": [],
"source": [
"db = database('data/todos.db')\n",
"todos = db.t.todos\n",
"if todos not in db.t: todos.create(id=int, title=str, done=bool, pk='id')\n",
"Todo = todos.dataclass()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ec747851",
"metadata": {
"input_tokens": 6,
"output_tokens": 19,
"time_run": "4:30:02p"
},
"outputs": [
{
"data": {
"text/plain": [
"[]"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"db.t.todos()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1f151b48",
"metadata": {
"input_tokens": 28,
"time_run": "4:26:27p"
},
"outputs": [],
"source": [
"id_curr = 'current-todo'\n",
"def tid(id): return f'todo-{id}'"
]
},
{
"cell_type": "markdown",
"id": "3f85c149",
"metadata": {
"input_tokens": 51
},
"source": [
"To serve a FastHTML app in solveit, we need to use `nb_serve`. And we use `render_ft` to render fast tags in solveit."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0b2d118f",
"metadata": {
"input_tokens": 120,
"time_run": "4:26:29p"
},
"outputs": [],
"source": [
"if 'srv' not in globals() or not srv: # Checking it doesn't already exists for if we 'run all' - not always needed\n",
" css = Style(':root { --pico-font-size: 100%; }')\n",
" app = FastHTML(hdrs=(picolink, css))\n",
" rt = app.route\n",
" srv = nb_serve(app)\n",
"\n",
"render_ft()"
]
},
{
"cell_type": "markdown",
"id": "bf06e09b",
"metadata": {
"input_tokens": 100
},
"source": [
"Here are some things we have to do to run the app in solveit:\n",
"\n",
"- Change routes from `/` to something else because solveit uses `/`. We will use `/hp`.\n",
"- Only GET and POST methods work. To DELETE or PUT, we will use POST.\n",
"\n",
"We can use dialoghelper to find and replace those routes."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "49dadcec",
"metadata": {
"input_tokens": 40,
"time_run": "4:26:34p"
},
"outputs": [],
"source": [
"@rt(\"/{fname:path}.{ext:static}\")\n",
"def get(fname:str, ext:str): return FileResponse(f'{fname}.{ext}')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "54457332",
"metadata": {
"input_tokens": 117,
"time_run": "4:26:38p"
},
"outputs": [],
"source": [
"@patch\r\n",
"def __ft__(self:Todo):\r\n",
" show = AX(self.title, f'/todos/{self.id}', id_curr)\r\n",
" edit = AX('edit', f'/edit/{self.id}' , id_curr)\r\n",
" dt = ' (done)' if self.done else ''\r\n",
" return Li(show, dt, ' | ', edit, id=tid(self.id))\r"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "69a97297",
"metadata": {
"input_tokens": 22,
"output_tokens": 57,
"time_run": "4:26:39p"
},
"outputs": [
{
"data": {
"text/markdown": [
"<div>\n",
"<a href=\"#\" hx-get=\"https://www.google.com/\">click bait</a><script>if (window.htmx) htmx.process(document.body)</script></div>\n"
],
"text/plain": [
"a(('click bait',),{'href': '#', 'hx-get': 'https://www.google.com/'})"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"AX('click bait', 'https://www.google.com/', 0)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1975ac07",
"metadata": {
"input_tokens": 42,
"output_tokens": 64,
"time_run": "4:26:42p"
},
"outputs": [
{
"data": {
"text/markdown": [
"<div>\n",
" <input name=\"title\" placeholder=\"New Todo\" id=\"new-title\" class=\"uk-input \">\n",
"<script>if (window.htmx) htmx.process(document.body)</script></div>\n"
],
"text/plain": [
"input((),{'name': 'title', 'placeholder': 'New Todo', 'id': 'new-title', 'class': 'uk-input '})"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def mk_input(**kw): return Input(id=\"new-title\", name=\"title\", placeholder=\"New Todo\", **kw)\n",
"mk_input()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2845f02f",
"metadata": {
"input_tokens": 28,
"time_run": "4:26:44p"
},
"outputs": [],
"source": [
"def clr_details(): return Div(hx_swap_oob='innerHTML', id=id_curr)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "272887fd",
"metadata": {
"input_tokens": 114,
"time_run": "4:26:45p"
},
"outputs": [],
"source": [
"@rt(\"/hp\")\n",
"def get(request):\n",
" add = Form(Group(mk_input(), Button(\"Add\")),\n",
" hx_post=\"/hp\", target_id='todo-list', hx_swap=\"afterbegin\")\n",
" card = Card(Ul(*todos(), id='todo-list'),\n",
" header=add, footer=Div(id=id_curr)),\n",
" return Titled('Todo list', card)"
]
},
{
"cell_type": "markdown",
"id": "d1e0e55e",
"metadata": {
"input_tokens": 69
},
"source": [
"After we setup `get` into `/hp` route, we can check our web app running in a new tab. Go to `https://whatever_secret_stuff.solveit.fast.ai/hp` and check the todo app."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4b6cd296",
"metadata": {
"input_tokens": 39,
"time_run": "4:27:23p"
},
"outputs": [],
"source": [
"@rt(\"/hp\")\n",
"def post(todo:Todo): return todos.insert(todo), mk_input(hx_swap_oob='true')"
]
},
{
"cell_type": "markdown",
"id": "00225d90",
"metadata": {
"input_tokens": 24
},
"source": [
"After seting the `post` to `/hp`, try adding a todo."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "901f1543",
"metadata": {
"input_tokens": 103,
"time_run": "4:28:18p"
},
"outputs": [],
"source": [
"@rt(\"/edit/{id}\")\n",
"def get(id:int):\n",
" res = Form(Group(Input(id=\"title\"), Button(\"Save\")),\n",
" Hidden(id=\"id\"), CheckboxX(id=\"done\", label='Done'),\n",
" hx_post=\"/edit/todos\", target_id=tid(id), id=\"edit\")\n",
" return fill_form(res, todos[id])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e858ee7f",
"metadata": {
"input_tokens": 31,
"time_run": "4:28:20p"
},
"outputs": [],
"source": [
"@rt(\"/edit/todos\")\n",
"def post(todo: Todo): return todos.update(todo), clr_details()"
]
},
{
"cell_type": "markdown",
"id": "db9b40d2",
"metadata": {
"input_tokens": 15
},
"source": [
"Now we can click the edit button and edit."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a2196650",
"metadata": {
"input_tokens": 36,
"time_run": "4:29:02p"
},
"outputs": [],
"source": [
"@rt(\"/delete/todos/{id}\")\n",
"def post(id:int):\n",
" todos.delete(id)\n",
" return clr_details()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2300f91",
"metadata": {
"input_tokens": 90,
"time_run": "4:29:04p"
},
"outputs": [],
"source": [
"@rt(\"/todos/{id}\")\n",
"def get(id:int):\n",
" todo = todos[id]\n",
" btn = Button('delete', hx_post=f'/delete/todos/{todo.id}',\n",
" target_id=tid(todo.id), hx_swap=\"outerHTML\")\n",
" return Div(Div(todo.title), btn)"
]
},
{
"cell_type": "markdown",
"id": "a4c752b7",
"metadata": {
"input_tokens": 21
},
"source": [
"We can delete todos by clicking the todo and clicking the delete button."
]
}
],
"metadata": {
"solveit_dialog_mode": "learning",
"solveit_ver": 2
},
"nbformat": 4,
"nbformat_minor": 5
}
@jchenj
Copy link
Copy Markdown

jchenj commented Sep 22, 2025

Thanks for sharing this example, super helpful! As a heads-up, I just saw in a discord post from Jeremy on July 15th that we shouldn't pip install dialogue helper into solveit, so it seems like it would be good to remove this from the example: https://discord.com/channels/1303610061167263814/1310989166875508849/1394757116891238511

@galopyz
Copy link
Copy Markdown
Author

galopyz commented Sep 22, 2025

@jchenj

Thanks for reading and giving me a heads up. I am using the version automatically deployed in solveit. I did not pip install dialoguehelper. I encourage you to try it out. It is a very fun tool set.

@jchenj
Copy link
Copy Markdown

jchenj commented Sep 22, 2025

ah sorry about that! understood and will definitely try it out! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment