Files
patchy631--ai-engineering-hub/deepseek-thinking-ui/app.py
T
2026-07-13 12:37:47 +08:00

121 lines
4.4 KiB
Python

import re
import base64
import streamlit as st
from ollama import chat
# Set Streamlit page configuration (optional)
st.set_page_config(page_title="Ollama Streaming Chat", layout="centered")
def format_reasoning_response(thinking_content):
"""Format assistant content by removing think tags."""
return (
thinking_content.replace("<think>\n\n</think>", "")
.replace("<think>", "")
.replace("</think>", "")
)
def display_message(message):
"""Display a single message in the chat interface."""
role = "user" if message["role"] == "user" else "assistant"
with st.chat_message(role):
if role == "assistant":
display_assistant_message(message["content"])
else:
st.markdown(message["content"])
def display_assistant_message(content):
"""Display assistant message with thinking content if present."""
pattern = r"<think>(.*?)</think>"
think_match = re.search(pattern, content, re.DOTALL)
if think_match:
think_content = think_match.group(0)
response_content = content.replace(think_content, "")
think_content = format_reasoning_response(think_content)
with st.expander("Thinking complete!"):
st.markdown(think_content)
st.markdown(response_content)
else:
st.markdown(content)
def display_chat_history():
"""Display all previous messages in the chat history."""
for message in st.session_state["messages"]:
if message["role"] != "system": # Skip system messages
display_message(message)
def process_thinking_phase(stream):
"""Process the thinking phase of the assistant's response."""
thinking_content = ""
with st.status("Thinking...", expanded=True) as status:
think_placeholder = st.empty()
for chunk in stream:
content = chunk["message"]["content"] or ""
thinking_content += content
if "<think>" in content:
continue
if "</think>" in content:
content = content.replace("</think>", "")
status.update(label="Thinking complete!", state="complete", expanded=False)
break
think_placeholder.markdown(format_reasoning_response(thinking_content))
return thinking_content
def process_response_phase(stream):
"""Process the response phase of the assistant's response."""
response_placeholder = st.empty()
response_content = ""
for chunk in stream:
content = chunk["message"]["content"] or ""
response_content += content
response_placeholder.markdown(response_content)
return response_content
@st.cache_resource
def get_chat_model():
"""Get a cached instance of the chat model."""
return lambda messages: chat(
model="deepseek-r1",
messages=messages,
stream=True,
)
def handle_user_input():
"""Handle new user input and generate assistant response."""
if user_input := st.chat_input("Type your message here..."):
st.session_state["messages"].append({"role": "user", "content": user_input})
with st.chat_message("user"):
st.markdown(user_input)
with st.chat_message("assistant"):
chat_model = get_chat_model()
stream = chat_model(st.session_state["messages"])
thinking_content = process_thinking_phase(stream)
response_content = process_response_phase(stream)
# Save the complete response
st.session_state["messages"].append(
{"role": "assistant", "content": thinking_content + response_content}
)
def main():
"""Main function to handle the chat interface and streaming responses."""
st.markdown("""
# Mini ChatGPT powered by <img src="data:image/png;base64,{}" width="170" style="vertical-align: -3px;">
""".format(base64.b64encode(open("assets/deep-seek.png", "rb").read()).decode()), unsafe_allow_html=True)
st.markdown("<h4 style='text-align: center;'>With thinking UI! 💡</h4>", unsafe_allow_html=True)
display_chat_history()
handle_user_input()
if __name__ == "__main__":
# Initialize session state
if "messages" not in st.session_state:
st.session_state["messages"] = [
{"role": "system", "content": "You are a helpful assistant."}
]
main()