-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This example enables browser chat with context history using local DeepSeek-R1 1.5B model.
- Loading branch information
Showing
8 changed files
with
266 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Chat session web server using Ollama's Python API and DeepSeek-R1 1.5B model. | ||
|
||
Instructions are provided in a [top level README section](/README.md#web-server). | ||
|
||
A browser chat session example. | ||
|
||
<img src="../images/chat_browser_example.png" alt="Web browser example"/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
"""A simple web-based chat application using Flask and Ollama. | ||
This application allows users to interact with a locally running DeepSeek model | ||
""" | ||
|
||
import markdown | ||
from flask import Flask, render_template, request | ||
from ollama import chat as ollama_chat | ||
|
||
VERBOSE = False | ||
|
||
app = Flask(__name__) | ||
|
||
messages = [] | ||
|
||
|
||
def get_chat_response(user_input: str) -> str: | ||
"""Get response from locally running Ollama model.""" | ||
try: | ||
messages.append({"role": "user", "content": user_input}) | ||
content = "" | ||
for part in ollama_chat( | ||
model="deepseek-r1:1.5b", | ||
messages=messages, | ||
options={ | ||
"seed": 42, | ||
"temperature": 0.6, | ||
}, | ||
stream=True, | ||
): | ||
chunk = part["message"]["content"] | ||
content += chunk | ||
if VERBOSE: | ||
print(chunk, end="", flush=True) | ||
print() | ||
messages.append({"role": "assistant", "content": content}) | ||
|
||
# Process the content to handle <think> blocks and markdown in HTML. | ||
|
||
# First, protect <think> blocks by replacing them temporarily | ||
think_blocks = [] | ||
import re | ||
|
||
def save_think_block(match): | ||
think_blocks.append(match.group(0)) | ||
return f"THINK_BLOCK_{len(think_blocks) - 1}" | ||
|
||
# Save think blocks and replace with placeholders | ||
content_with_placeholders = re.sub( | ||
r"<think>[\s\S]*?</think>", save_think_block, content | ||
) | ||
|
||
# Convert markdown to HTML | ||
processed_content = markdown.markdown( | ||
content_with_placeholders, extensions=["fenced_code"] | ||
) | ||
|
||
# Restore think blocks | ||
for i, block in enumerate(think_blocks): | ||
processed_content = processed_content.replace( | ||
f"<p>THINK_BLOCK_{i}</p>", block | ||
) | ||
if VERBOSE: | ||
print("Processed content:") | ||
print(processed_content) | ||
return processed_content | ||
except Exception as e: | ||
print(f"Error getting response from Ollama: {e}") | ||
return "Sorry, please try again." | ||
|
||
|
||
@app.route("/", methods=["GET", "POST"]) | ||
def index() -> str: | ||
"""Handle main page requests.""" | ||
if request.method != "POST": | ||
return render_template("index.html", user_input="", bot_response="") | ||
|
||
submitted_input = request.form.get("user_input", "") | ||
chat_response = get_chat_response(submitted_input) | ||
return render_template( | ||
"index.html", | ||
last_question=submitted_input, | ||
bot_response=chat_response, | ||
) | ||
|
||
|
||
@app.route("/chat", methods=["POST"]) | ||
def chat(): | ||
"""Handle chat API requests.""" | ||
submitted_input = request.form.get("user_input", "") | ||
chat_response = get_chat_response(submitted_input) | ||
return {"response": chat_response, "last_question": submitted_input} | ||
|
||
|
||
if __name__ == "__main__": | ||
app.run(debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Local chat using DeepSeek-R1 and Ollama</title> | ||
<style> | ||
body { font-family: Arial, sans-serif; background-color: #f0f0f0; margin: 0; padding: 0;} | ||
.container { width: 50%; margin: 0 auto; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } | ||
h1 { text-align: center; } | ||
.chat-box { margin-top: 0; border-top: 2px solid #ddd; padding-top: 20px;} | ||
.chat-box .user-message, .chat-box .bot-message { padding: 10px; margin: 10px 0; border-radius: 5px;} | ||
.user-message { background-color: #e1f5fe; text-align: right;} | ||
.bot-message { background-color: #f1f1f1; white-space: pre-wrap; padding: 10px; margin: 10px 0; border-radius: 5px; } | ||
.bot-message think { | ||
color: #666; | ||
font-style: italic; | ||
border-left: 3px solid #57b368; | ||
padding-left: 10px; | ||
margin: 5px 0; | ||
display: block; | ||
background-color: #f8f9fa; /* Lighter background to distinguish thinking steps */ | ||
white-space: pre-wrap; /* Preserve line breaks */ | ||
word-wrap: break-word; /* Break long words if needed */ | ||
overflow-wrap: break-word; /* Modern browsers */ | ||
} | ||
.bot-message pre { | ||
background-color: #f8f9fa; | ||
padding: 10px; | ||
border-radius: 4px; | ||
overflow-x: auto; | ||
} | ||
.bot-message code { | ||
font-family: 'Courier New', Courier, monospace; | ||
background-color: #f8f9fa; | ||
padding: 2px 4px; | ||
border-radius: 3px; | ||
} | ||
.bot-message p { | ||
margin: 0 0 1em 0; | ||
} | ||
.bot-message ul, .bot-message ol { | ||
margin-left: 20px; | ||
} | ||
form { margin-top: 20px; margin-bottom: 20px; } | ||
input[type="text"] { width: 80%; padding: 10px; border-radius: 5px; border: 1px solid #ddd;} | ||
button { padding: 10px 20px; border: none; background-color: #007BFF; color: white; border-radius: 5px; cursor: pointer;} | ||
#thinking { | ||
display: none; | ||
color: #666; | ||
font-style: italic; | ||
padding: 10px; | ||
margin: 10px 0; | ||
background-color: #f8f9fa; | ||
border-left: 3px solid #57b368; | ||
} | ||
.dot-flashing { | ||
display: inline-block; | ||
position: relative; | ||
width: 4px; | ||
height: 4px; | ||
background-color: #666; | ||
border-radius: 50%; | ||
animation: dot-flashing 1s infinite linear alternate; | ||
margin-left: 8px; | ||
} | ||
@keyframes dot-flashing { | ||
0% { opacity: 1; } | ||
50% { opacity: 0.5; } | ||
100% { opacity: 0; } | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div class="container"> | ||
<h1>Reasoning with DeepSeek-R1</h1> | ||
<form id="chat-form" method="POST"> | ||
<input type="text" | ||
name="user_input" | ||
placeholder="Ask me something that needs reasoning..." | ||
value="" | ||
required> | ||
<button type="submit">Send</button> | ||
</form> | ||
<div id="thinking"> | ||
Thinking<span class="dot-flashing"></span> | ||
</div> | ||
<div class="chat-box"> | ||
{% if last_question %} | ||
<div class="user-message">{{ last_question }}</div> | ||
<div class="bot-message">{{ bot_response|safe }}</div> | ||
{% endif %} | ||
</div> | ||
</div> | ||
|
||
<script> | ||
document.getElementById('chat-form').addEventListener('submit', async function(e) { | ||
e.preventDefault(); | ||
const form = e.target; | ||
const input = form.querySelector('input'); | ||
const thinking = document.getElementById('thinking'); | ||
const chatBox = document.querySelector('.chat-box'); | ||
|
||
// Show thinking indicator | ||
thinking.style.display = 'block'; | ||
|
||
try { | ||
const formData = new FormData(form); | ||
const response = await fetch('/chat', { | ||
method: 'POST', | ||
body: formData | ||
}); | ||
const data = await response.json(); | ||
|
||
// Add new messages to chat box | ||
chatBox.innerHTML = ` | ||
<div class="user-message">${data.last_question}</div> | ||
<div class="bot-message">${data.response}</div> | ||
${chatBox.innerHTML} | ||
`; | ||
|
||
// Clear input | ||
input.value = ''; | ||
} catch (error) { | ||
console.error('Error:', error); | ||
} finally { | ||
// Hide thinking indicator | ||
thinking.style.display = 'none'; | ||
} | ||
}); | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
Flask | ||
markdown | ||
ollama |