Table of Contents
What We'll Build
By the end of this tutorial, you'll have built a fully functional AI chatbot with these features:
- Real-time streaming - See responses appear word by word
- Conversation memory - Bot remembers context across messages
- Multi-turn dialogue - Natural back-and-forth conversations
- Web interface - Clean, responsive chat UI
- Error handling - Graceful fallbacks when API fails
Our chatbot will use DeepSeek V4 via the TokenEase API gateway, giving you access to one of the most capable Chinese AI models with OpenAI-compatible endpoints.
Prerequisites
- Python 3.8+ installed
- A TokenEase API key (sign up for free - $1 credit included)
- Basic knowledge of Python and HTTP APIs
Project Setup
Let's create our project structure:
mkdir deepseek-chatbot
cd deepseek-chatbot
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install flask requests python-dotenv
Create a .env file with your API key:
TOKENEASE_API_KEY=your_api_key_here
MODEL=deepseek
Basic Chat Implementation
Let's start with a simple script that sends a message and gets a response:
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("TOKENEASE_API_KEY")
API_URL = "https://tokenease.io/v1/chat/completions"
def chat(message, history=None):
if history is None:
history = []
messages = history + [{"role": "user", "content": message}]
response = requests.post(
API_URL,
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={
"model": os.getenv("MODEL", "deepseek"),
"messages": messages,
"temperature": 0.7,
"max_tokens": 2000
}
)
if response.status_code == 200:
return response.json()["choices"][0]["message"]["content"]
else:
return f"Error: {response.status_code} - {response.text}"
# Test it
if __name__ == "__main__":
reply = chat("Hello! What can you help me with?")
print(reply)
Adding Streaming Responses
Streaming makes the chatbot feel much more responsive. Here's how to implement it:
import json
def chat_streaming(message, history=None):
if history is None:
history = []
messages = history + [{"role": "user", "content": message}]
response = requests.post(
API_URL,
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={
"model": os.getenv("MODEL", "deepseek"),
"messages": messages,
"stream": True, # Enable streaming
"temperature": 0.7,
"max_tokens": 2000
},
stream=True
)
full_response = ""
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: '):
data = line[6:]
if data == '[DONE]':
break
try:
chunk = json.loads(data)
if chunk['choices'][0]['delta'].get('content'):
content = chunk['choices'][0]['delta']['content']
full_response += content
print(content, end='', flush=True)
except:
pass
return full_response
# Test streaming
if __name__ == "__main__":
print("Bot: ", end='', flush=True)
chat_streaming("Explain quantum computing in simple terms")
Conversation Memory
To make conversations natural, we need to maintain context. Here's a simple memory implementation:
class ChatBot:
def __init__(self, max_history=10):
self.history = []
self.max_history = max_history
def add_message(self, role, content):
self.history.append({"role": role, "content": content})
# Keep only recent messages to manage token usage
if len(self.history) > self.max_history * 2:
self.history = self.history[-self.max_history * 2:]
def chat(self, message, stream=False):
self.add_message("user", message)
if stream:
response = self._chat_streaming(self.history)
else:
response = self._chat_basic(self.history)
self.add_message("assistant", response)
return response
def _chat_basic(self, messages):
# Same as earlier chat() function
response = requests.post(API_URL, headers={...}, json={...})
return response.json()["choices"][0]["message"]["content"]
def clear_history(self):
self.history = []
# Usage
bot = ChatBot(max_history=10)
print(bot.chat("Hi, I'm Bob"))
print(bot.chat("What's my name?")) # Remembers you're Bob!
max_history based on your expected conversation length. For long conversations, consider summarizing older messages.
Simple Web UI
Let's create a web interface using Flask:
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
bot = ChatBot(max_history=10)
@app.route("/")
def home():
return render_template("chat.html")
@app.route("/chat", methods=["POST"])
def chat_endpoint():
data = request.json
message = data.get("message", "")
if not message:
return jsonify({"error": "No message provided"}), 400
try:
response = bot.chat(message, stream=False)
return jsonify({"response": response})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/clear", methods=["POST"])
def clear():
bot.clear_history()
return jsonify({"status": "cleared"})
if __name__ == "__main__":
app.run(debug=True, port=5000)
Create templates/chat.html:
<!DOCTYPE html>
<html>
<head>
<title>DeepSeek Chatbot</title>
<style>
body { max-width: 800px; margin: 0 auto; padding: 20px; font-family: Arial; }
#chat { border: 1px solid #ddd; height: 400px; overflow-y: auto; padding: 20px; margin-bottom: 20px; }
.message { margin: 10px 0; padding: 10px; border-radius: 8px; }
.user { background: #e3f2fd; text-align: right; }
.bot { background: #f5f5f5; }
#input-area { display: flex; gap: 10px; }
input { flex: 1; padding: 10px; }
button { padding: 10px 20px; background: #2563eb; color: white; border: none; cursor: pointer; }
</style>
</head>
<body>
<h1>DeepSeek Chatbot</h1>
<div id="chat"></div>
<div id="input-area">
<input type="text" id="message" placeholder="Type your message...">
<button onclick="send()">Send</button>
<button onclick="clearChat()">Clear</button>
</div>
<script>
async function send() {
const input = document.getElementById('message');
const msg = input.value;
if (!msg) return;
addMessage('user', msg);
input.value = '';
const res = await fetch('/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: msg})
});
const data = await res.json();
addMessage('bot', data.response);
}
function addMessage(role, text) {
const chat = document.getElementById('chat');
chat.innerHTML += `<div class="message ${role}">${text}</div>`;
chat.scrollTop = chat.scrollHeight;
}
async function clearChat() {
await fetch('/clear', {method: 'POST'});
document.getElementById('chat').innerHTML = '';
}
document.getElementById('message').addEventListener('keypress', e => {
if (e.key === 'Enter') send();
});
</script>
</body>
</html>
Production Considerations
Before deploying to production, consider these improvements:
| Concern | Solution |
|---|---|
| Rate limiting | Implement request throttling per user |
| Error handling | Add retries with exponential backoff |
| Security | Validate inputs, sanitize outputs, use HTTPS |
| Cost control | Set max_tokens, monitor usage via dashboard |
| Scalability | Use async frameworks (FastAPI) for high concurrency |
| Monitoring | Log conversations, track error rates |
Advanced: Switching Models
With TokenEase, switching between models is trivial:
# Use GLM-5 instead of DeepSeek
os.environ["MODEL"] = "zhipu"
# Or Doubao for speed
os.environ["MODEL"] = "doubao"
# Or Kimi for long context
os.environ["MODEL"] = "kimi"
# Same code, different model - that's the power of a unified API
- DeepSeek - Best reasoning and coding
- GLM-5 - Strong Chinese language understanding
- Doubao - Fastest response times
- Kimi - Longest context window (up to 2M tokens)
- Qwen - Excellent multilingual capabilities
Ready to Build Your Chatbot?
Get started with $1 free credit - no credit card required
Access DeepSeek, GLM, Qwen, Kimi & Doubao with one API key
Get Free API Key →