Using Django, we'll add inline AI suggestions to suggest helpful actions to the user.
We'll use a fast LLM, HTMX, and Photon Designer.
Here's how our final product will look:
And here's a video guide (following the below written guide) featuring me 🙂:
0. Which LLM should I use to generate AI suggestions?
First, you need to choose your LLM. The key criteria are speed and being a warm model (meaning no boot required).
I'm using Llama 3 8B. Primarily because it's warm, fast, and a negligible cost. Secondarily because I like llamas 🦙
Choose whichever model you like. As of writing, Snowflake Arctic and Mixtral are also good choices on Replicate.
We'll do this in 7 short steps. Let's go 🏎️
1. Setup Django
Create Django app and install dependencies
pip install --upgrade django python-dotenv replicate
django-admin startproject core .
python manage.py startapp sim
Add our app sim to the INSTALLED_APPS in settings.py:
# settings.py
INSTALLED_APPS = [
'sim',
...
]
Add environment variables
- Add this to the top of your settings.py to load your environment variables:
# settings.py
from pathlib import Path
import os
from dotenv import load_dotenv
load_dotenv()
if not os.environ.get('REPLICATE_API_TOKEN'):
raise ValueError(
'REPLICATE_API_TOKEN environment variable is not set. '
'Please make sure you have added it to "core/.env" file, and restart the server.'
)
- Get your API token from Replicate
- Create a file called
.env
incore
and add your API token (No speech marks needed):
```.dotenv
REPLICATE_API_TOKEN=an_example_api_token
2. Add your Django app urls
- Add the following to your core/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('sim.urls')),
]
- Create a
urls.py
file in your sim app and add the following:
from django.contrib import admin
from django.urls import path, include
from sim import views
urlpatterns = [
path('', views.index, name='index'),
path('suggest', views.suggest, name='suggest'),
]
3. Add your views
- Add the below views to your sim app (
views.py
):
from django.shortcuts import render
from random import randint
from django.utils.html import format_html
import replicate
def index(request):
if request.method == 'GET':
return render(request, 'index.html')
elif request.method == 'POST':
return render(request, 'index.html')
def suggest(request):
text = request.POST.get('text', '')
if text:
input = {
"prompt":
f"Complete the following sentence. Don't write more than 1 sentence.\n----\n"
f"{text}",
"prompt_template": "<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\n"
"You are a helpful assistant. Don't include the start of the sentence. "
"Only include your completion. "
"<|eot_id|><|start_header_id|>user<|end_header_id|>\n\n{prompt}"
"<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n",
"presence_penalty": 0,
"frequency_penalty": 0
}
output = replicate.run(
"meta/meta-llama-3-8b-instruct",
input=input
)
completion = "".join(output)
else:
completion = ""
return render(request, 'suggestion.html', {"suggestion": completion.strip()})
4. Add templates
- Add a
templates
folder to your sim app - Add a
suggestion.html
file to yourtemplates
folder and add the following:
<span>
{{ suggestion }}
</span>
- Add a
index.html
files to yourtemplates
folder and add the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inline Suggestion</title>
<script src="https://unpkg.com/htmx.org@1.9.12"></script>
</head>
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<div>
<form id="container" tabindex="0" hx-trigger="input delay:200ms"
hx-post="/suggest"
hx-target="#suggestion"
>
<div>
<div id="display" contenteditable="true"
autofocus
oninput="syncInput(this.textContent);clearSuggestion();">
</div>
<div id="suggestion"
onclick="acceptSuggestion(this.textContent); clearSuggestion(); continueWriting();"
>
{% include 'suggestion.html' %}
</div>
</div>
<input name="text" type="hidden" id="content" value="">
</form>
</div>
<script>
const clearSuggestion = () => {
document.getElementById('suggestion').innerHTML = '';
toggleSuggestionVisibility();
}
const acceptSuggestion = (textContent) => {
document.getElementById('display').textContent += textContent
syncInput(textContent)
}
const syncInput = (textContent) => {
document.getElementById('content').value = textContent
}
const continueWriting = () => {
document.getElementById('display').focus()
const range = document.createRange()
const selection = window.getSelection()
range.selectNodeContents(document.getElementById('display'))
range.collapse(false)
selection.removeAllRanges()
selection.addRange(range)
}
const toggleSuggestionVisibility = () => {
const suggestionBox = document.getElementById('suggestion');
if (suggestionBox.textContent.trim() === '') {
suggestionBox.classList.remove('show');
suggestionBox.classList.add('hide');
} else {
suggestionBox.classList.remove('hide');
suggestionBox.classList.add('show');
}
}
document.getElementById('suggestion').addEventListener('DOMNodeInserted', toggleSuggestionVisibility);
document.getElementById('suggestion').addEventListener('DOMNodeRemoved', toggleSuggestionVisibility);
</script>
</body>
</html>
5. Run our server and visit the url
- Let's see the unstyled version of our app:
python manage.py runserver
6. Style the UI with Photon Designer?
Currently, the UI is very basic:
I'll improve it by using 'Django Mode' in Photon Designer (my product). I pasted the code into Photon Designer, clicked 'Django Mode', and gave some prompts such "Generate amazing UI for this inline-ai suggestion component. Add masterful details. Simplify my existing code"
And the final template code (after using Photon Designer) for index.html:
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inline Suggestion</title>
<script src="https://unpkg.com/htmx.org@1.9.12"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>
.suggestion-box {
transition: all 0.3s ease-in-out;
}
.suggestion-box.show {
opacity: 1;
transform: translateY(5px);
}
.suggestion-box.hide {
opacity: 0;
transform: translateY(20px);
}
</style>
</head>
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' class="h-full bg-gray-100">
<div class="flex justify-center items-center h-screen">
<form id="container" tabindex="0" hx-trigger="input delay:200ms"
hx-post="/suggest"
hx-target="#suggestion"
class="flex flex-col items-start w-1/2"
>
<div class="relative w-full">
<div id="display" contenteditable="true"
class="w-full p-4 text-gray-800 bg-white border border-gray-300 rounded-lg shadow-md focus:outline-none focus:ring-1 focus:ring-indigo-300 transition-all duration-300"
autofocus
oninput="syncInput(this.textContent);clearSuggestion();">
</div>
<div id="suggestion"
class="suggestion-box absolute left-0 mb-2 bg-white border border-gray-300 rounded-lg shadow-md p-4 max-w-full text-ellipsis cursor-pointer transition-all duration-300 hide"
onclick="acceptSuggestion(this.textContent); clearSuggestion(); continueWriting();"
>
{% include 'suggestion.html' %}
</div>
</div>
<input name="text" type="hidden" id="content" value="">
</form>
</div>
<script>
const clearSuggestion = () => {
document.getElementById('suggestion').innerHTML = '';
toggleSuggestionVisibility();
}
const acceptSuggestion = (textContent) => {
document.getElementById('display').textContent += textContent
syncInput(textContent)
}
const syncInput = (textContent) => {
document.getElementById('content').value = textContent
}
const continueWriting = () => {
document.getElementById('display').focus()
const range = document.createRange()
const selection = window.getSelection()
range.selectNodeContents(document.getElementById('display'))
range.collapse(false)
selection.removeAllRanges()
selection.addRange(range)
}
const toggleSuggestionVisibility = () => {
const suggestionBox = document.getElementById('suggestion');
if (suggestionBox.textContent.trim() === '') {
suggestionBox.classList.remove('show');
suggestionBox.classList.add('hide');
} else {
suggestionBox.classList.remove('hide');
suggestionBox.classList.add('show');
}
}
document.getElementById('suggestion').addEventListener('DOMNodeInserted', toggleSuggestionVisibility);
document.getElementById('suggestion').addEventListener('DOMNodeRemoved', toggleSuggestionVisibility);
</script>
</body>
</html>
Complete ✅ AI suggestions added to your Django app
We have successfully added inline AI suggestions to our Django app. We used the Replicate API to generate the suggestions. We also improved the UI using Photon Designer. You're welcome to customize the UI further or use a different model for the suggestions.
I'll be adding this into Photon Designer to show UI prompts to users.
Here's the final product again: