Python调用DeepSeek大模型API及前后端页面制作
·
作为国产AI模型的佼佼者,DeepSeek凭借流畅的中文理解能力和行业解决方案,正在成为开发者心中的"新宠"。其开放的API接口,让普通程序员也能轻松搭建智能对话系统。
注意需要替换对应API
一、Python调用DeepSeek API代码
# 安装 OpenAI SDK:pip install openai
from openai import OpenAI
# 创建 API 客户端
client = OpenAI(api_key="sk-xxxxxxxxxxxxxxx", base_url="https://api.deepseek.com")
# 调用 deepseek-chat 模型
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "你好?"},
],
stream=False # 设置为 True 可启用流式输出
)
# 输出响应内容
print(response.choices[0].message.content)
二、DeepSeek+tkinter(一)
import tkinter as tk
from tkinter import scrolledtext, messagebox
import threading
from openai import OpenAI
class DeepSeekChatGUI:
def __init__(self, root):
self.root = root
self.root.title("DeepSeek Chat 客户端")
self.root.geometry("800x600")
self.root.configure(bg="#f0f0f0")
# 创建API客户端
self.client = OpenAI(api_key="sk-xxxxxxxxxx", base_url="https://api.deepseek.com")
self.setup_ui()
def setup_ui(self):
# 标题
title_label = tk.Label(self.root, text="DeepSeek Chat", font=("Arial", 16, "bold"), bg="#f0f0f0")
title_label.pack(pady=10)
# 聊天历史显示区域
self.chat_history = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, width=70, height=20, font=("Arial", 10))
self.chat_history.pack(padx=20, pady=10, fill=tk.BOTH, expand=True)
self.chat_history.config(state=tk.DISABLED)
# 输入区域框架
input_frame = tk.Frame(self.root, bg="#f0f0f0")
input_frame.pack(padx=20, pady=10, fill=tk.X)
# 用户输入框
self.user_input = scrolledtext.ScrolledText(input_frame, wrap=tk.WORD, width=50, height=3, font=("Arial", 10))
self.user_input.pack(side=tk.LEFT, padx=(0, 10), fill=tk.X, expand=True)
# 发送按钮
send_button = tk.Button(input_frame, text="发送", command=self.send_message, bg="#4CAF50", fg="white",
font=("Arial", 10, "bold"), width=10, height=2)
send_button.pack(side=tk.RIGHT)
# 绑定回车键发送消息
self.user_input.bind("<Control-Return>", lambda event: self.send_message())
# 添加使用提示
tip_label = tk.Label(self.root, text="提示: 使用 Ctrl+Enter 快速发送消息",
font=("Arial", 9), fg="gray", bg="#f0f0f0")
tip_label.pack(pady=5)
def send_message(self):
user_message = self.user_input.get("1.0", tk.END).strip()
if not user_message:
messagebox.showwarning("输入错误", "请输入消息内容")
return
# 禁用输入框和按钮
self.user_input.config(state=tk.DISABLED)
# 显示用户消息
self.update_chat_history("用户", user_message)
# 清空输入框
self.user_input.delete("1.0", tk.END)
# 在新线程中调用API,避免界面冻结
threading.Thread(target=self.call_deepseek_api, args=(user_message,), daemon=True).start()
def call_deepseek_api(self, user_message):
try:
# 显示"正在思考..."提示
self.root.after(0, self.show_thinking)
# 调用API
response = self.client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": user_message},
],
stream=False
)
# 获取回复内容
assistant_reply = response.choices[0].message.content
# 更新聊天历史
self.root.after(0, self.update_chat_history, "助手", assistant_reply)
except Exception as e:
error_msg = f"API调用出错: {str(e)}"
self.root.after(0, self.update_chat_history, "系统", error_msg)
finally:
# 重新启用输入框
self.root.after(0, self.enable_input)
def show_thinking(self):
self.update_chat_history("助手", "正在思考...")
def update_chat_history(self, sender, message):
self.chat_history.config(state=tk.NORMAL)
# 添加发送者标签
if sender == "用户":
self.chat_history.insert(tk.END, "您: ", "user")
elif sender == "助手":
self.chat_history.insert(tk.END, "DeepSeek: ", "assistant")
else:
self.chat_history.insert(tk.END, "系统: ", "system")
# 添加消息内容
self.chat_history.insert(tk.END, message + "\n\n")
# 配置标签样式
self.chat_history.tag_config("user", foreground="blue")
self.chat_history.tag_config("assistant", foreground="green")
self.chat_history.tag_config("system", foreground="red")
# 滚动到底部
self.chat_history.see(tk.END)
self.chat_history.config(state=tk.DISABLED)
def enable_input(self):
self.user_input.config(state=tk.NORMAL)
self.user_input.focus()
if __name__ == "__main__":
root = tk.Tk()
app = DeepSeekChatGUI(root)
root.mainloop()

对应html页面代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DeepSeek Chat 客户端</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial', 'Microsoft YaHei', sans-serif;
}
body {
background-color: #f5f7fa;
color: #333;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* 标题区域 */
.header {
background-color: #2c3e50;
color: white;
padding: 1rem 2rem;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.header h1 {
font-size: 1.8rem;
font-weight: 600;
}
/* 聊天容器 */
.chat-container {
flex: 1;
max-width: 1000px;
width: 100%;
margin: 1.5rem auto;
padding: 0 1rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
/* 聊天历史区域 */
.chat-history {
flex: 1;
background-color: white;
border-radius: 10px;
padding: 1.5rem;
overflow-y: auto;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
min-height: 400px;
max-height: 600px;
}
/* 滚动条样式优化 */
.chat-history::-webkit-scrollbar {
width: 8px;
}
.chat-history::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.chat-history::-webkit-scrollbar-thumb {
background: #bbb;
border-radius: 4px;
}
.chat-history::-webkit-scrollbar-thumb:hover {
background: #999;
}
/* 消息气泡样式 */
.message {
margin-bottom: 1.2rem;
padding: 0.8rem 1.2rem;
border-radius: 8px;
max-width: 80%;
line-height: 1.5;
}
.message-user {
background-color: #e3f2fd;
color: #1976d2;
margin-left: auto;
}
.message-assistant {
background-color: #e8f5e9;
color: #2e7d32;
margin-right: auto;
}
.message-system {
background-color: #ffebee;
color: #c62828;
margin: 0 auto;
text-align: center;
max-width: 60%;
}
/* 消息发送者标签 */
.message-sender {
font-weight: 600;
margin-bottom: 0.3rem;
display: block;
}
/* 输入区域 */
.input-container {
display: flex;
gap: 1rem;
align-items: flex-end;
}
#user-input {
flex: 1;
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem;
min-height: 80px;
resize: none;
font-size: 1rem;
transition: border-color 0.3s;
}
#user-input:focus {
outline: none;
border-color: #2196f3;
box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);
}
#send-btn {
padding: 0.8rem 2rem;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s;
height: 50px;
}
#send-btn:hover {
background-color: #43a047;
}
#send-btn:disabled {
background-color: #a5d6a7;
cursor: not-allowed;
}
/* 提示信息 */
.tip {
text-align: center;
color: #666;
font-size: 0.9rem;
margin-bottom: 1rem;
}
/* 响应式调整 */
@media (max-width: 768px) {
.input-container {
flex-direction: column;
align-items: stretch;
}
#send-btn {
height: auto;
padding: 1rem;
}
.message {
max-width: 90%;
}
}
</style>
</head>
<body>
<!-- 标题区域 -->
<header class="header">
<h1>DeepSeek Chat 客户端</h1>
</header>
<!-- 主聊天容器 -->
<main class="chat-container">
<!-- 聊天历史显示区 -->
<div class="chat-history" id="chat-history"></div>
<!-- 使用提示 -->
<div class="tip">提示: 使用 Ctrl+Enter 快速发送消息 | 输入内容后点击发送按钮也可提交</div>
<!-- 输入和发送区域 -->
<div class="input-container">
<textarea
id="user-input"
placeholder="请输入您的问题或消息..."
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
></textarea>
<button id="send-btn" onclick="sendMessage()">发送</button>
</div>
</main>
<script>
// 配置信息(请替换为您自己的 API 密钥)
const API_CONFIG = {
apiKey: "sk-xxxxxxxxxx", // 替换为您的 DeepSeek API 密钥
baseUrl: "https://api.deepseek.com/v1/chat/completions", // DeepSeek API 端点
model: "deepseek-chat" // 使用的模型
};
// DOM 元素
const chatHistoryEl = document.getElementById('chat-history');
const userInputEl = document.getElementById('user-input');
const sendBtnEl = document.getElementById('send-btn');
// 初始化:绑定 Ctrl+Enter 事件
userInputEl.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault(); // 阻止默认换行行为
sendMessage();
}
});
/**
* 发送消息函数
*/
async function sendMessage() {
// 1. 获取并验证用户输入
const userMessage = userInputEl.value.trim();
if (!userMessage) {
alert('请输入消息内容后再发送!');
return;
}
// 2. 禁用输入和按钮,防止重复发送
disableInput(true);
// 3. 显示用户消息
addMessageToHistory('user', userMessage);
// 4. 清空输入框
userInputEl.value = '';
// 5. 显示"正在思考"提示
const thinkingMsgId = addMessageToHistory('assistant', '正在思考...', true);
try {
// 6. 调用 DeepSeek API
const response = await fetch(API_CONFIG.baseUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_CONFIG.apiKey}`
},
body: JSON.stringify({
model: API_CONFIG.model,
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: userMessage }
],
stream: false // 非流式响应(如需流式可改为 true 并处理)
})
});
// 7. 处理 API 响应
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error?.message || `API 请求失败 (状态码: ${response.status})`);
}
const data = await response.json();
const assistantReply = data.choices[0]?.message?.content || '未获取到有效回复';
// 8. 替换"正在思考"为实际回复
updateMessageInHistory(thinkingMsgId, assistantReply);
} catch (error) {
// 9. 处理错误
updateMessageInHistory(thinkingMsgId, `API调用出错: ${error.message}`, 'system');
} finally {
// 10. 重新启用输入和按钮
disableInput(false);
// 聚焦输入框,方便继续输入
userInputEl.focus();
}
}
/**
* 添加消息到聊天历史
* @param {string} sender 发送者类型: user/assistant/system
* @param {string} content 消息内容
* @param {boolean} isTemporary 是否为临时消息(如"正在思考")
* @returns {string} 消息元素的 ID(用于后续更新)
*/
function addMessageToHistory(sender, content, isTemporary = false) {
// 生成唯一 ID(用于临时消息更新)
const msgId = isTemporary ? `temp-${Date.now()}` : `msg-${Date.now()}`;
// 确定消息类型和发送者名称
let senderClass, senderName;
switch (sender) {
case 'user':
senderClass = 'message-user';
senderName = '您';
break;
case 'assistant':
senderClass = 'message-assistant';
senderName = 'DeepSeek';
break;
case 'system':
senderClass = 'message-system';
senderName = '系统';
break;
default:
senderClass = 'message-system';
senderName = '未知';
}
// 创建消息元素
const messageEl = document.createElement('div');
messageEl.id = msgId;
messageEl.className = `message ${senderClass}`;
messageEl.innerHTML = `
<span class="message-sender">${senderName}:</span>
<span class="message-content">${formatMessageContent(content)}</span>
`;
// 添加到历史区域并滚动到底部
chatHistoryEl.appendChild(messageEl);
scrollToBottom();
return msgId;
}
/**
* 更新历史中的消息(用于替换临时消息)
* @param {string} msgId 要更新的消息 ID
* @param {string} newContent 新的消息内容
* @param {string} newSender 可选:新的发送者类型(如错误时改为 system)
*/
function updateMessageInHistory(msgId, newContent, newSender = null) {
const messageEl = document.getElementById(msgId);
if (!messageEl) return;
// 更新内容
const contentEl = messageEl.querySelector('.message-content');
contentEl.textContent = newContent;
// 如果需要更改发送者类型(如错误场景)
if (newSender && newSender !== getSenderTypeFromClass(messageEl.className)) {
// 移除旧类,添加新类
messageEl.classList.remove(
'message-user', 'message-assistant', 'message-system'
);
messageEl.classList.add(`message-${newSender}`);
// 更新发送者名称
const senderEl = messageEl.querySelector('.message-sender');
senderEl.textContent = newSender === 'system' ? '系统:' : 'DeepSeek:';
}
scrollToBottom();
}
/**
* 禁用/启用输入框和发送按钮
* @param {boolean} isDisabled 是否禁用
*/
function disableInput(isDisabled) {
userInputEl.disabled = isDisabled;
sendBtnEl.disabled = isDisabled;
}
/**
* 滚动聊天历史到底部
*/
function scrollToBottom() {
chatHistoryEl.scrollTop = chatHistoryEl.scrollHeight;
}
/**
* 从消息元素的类名中获取发送者类型
* @param {string} className 元素的类名字符串
* @returns {string} 发送者类型: user/assistant/system
*/
function getSenderTypeFromClass(className) {
if (className.includes('message-user')) return 'user';
if (className.includes('message-assistant')) return 'assistant';
if (className.includes('message-system')) return 'system';
return '';
}
/**
* 格式化消息内容(处理换行等)
* @param {string} content 原始消息内容
* @returns {string} 格式化后的内容
*/
function formatMessageContent(content) {
// 替换 \n 为实际换行(避免 HTML 中不显示换行)
return content.replace(/\n/g, '\n');
}
</script>
</body>
</html>

三、DeepSeek+tkinter(二)
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
from openai import OpenAI
import threading
class DeepSeekChatGUI:
def __init__(self, root):
self.root = root
self.root.title("DeepSeek Chat Assistant")
self.root.geometry("800x600")
self.root.configure(bg="#f0f0f0")
# 创建API客户端
self.client = OpenAI(api_key="sk-xxxxxxxxxx", base_url="https://api.deepseek.com")
self.setup_ui()
def setup_ui(self):
# 主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 配置行列权重
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(1, weight=1)
main_frame.rowconfigure(1, weight=1)
# API密钥标签和输入框
ttk.Label(main_frame, text="API密钥:").grid(row=0, column=0, sticky=tk.W, pady=5)
self.api_key_var = tk.StringVar(value="sk-66c4e140194643b0b0b079f7d11395c8")
api_key_entry = ttk.Entry(main_frame, textvariable=self.api_key_var, width=50, show="*")
api_key_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5)
# 模型选择
ttk.Label(main_frame, text="模型:").grid(row=0, column=2, sticky=tk.W, padx=(10, 0), pady=5)
self.model_var = tk.StringVar(value="deepseek-chat")
model_combo = ttk.Combobox(main_frame, textvariable=self.model_var,
values=["deepseek-chat", "deepseek-coder"], width=15)
model_combo.grid(row=0, column=3, sticky=tk.W, pady=5)
# 对话历史显示区域
ttk.Label(main_frame, text="对话历史:").grid(row=1, column=0, sticky=tk.NW, pady=5)
self.conversation_text = scrolledtext.ScrolledText(main_frame, width=80, height=20, wrap=tk.WORD)
self.conversation_text.grid(row=1, column=1, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
self.conversation_text.config(state=tk.DISABLED)
# 用户输入区域
ttk.Label(main_frame, text="你的消息:").grid(row=2, column=0, sticky=tk.NW, pady=5)
self.user_input = scrolledtext.ScrolledText(main_frame, width=80, height=4, wrap=tk.WORD)
self.user_input.grid(row=2, column=1, columnspan=3, sticky=(tk.W, tk.E), pady=5)
# 按钮框架
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=3, column=1, columnspan=3, sticky=tk.E, pady=10)
# 发送按钮
self.send_button = ttk.Button(button_frame, text="发送", command=self.send_message)
self.send_button.pack(side=tk.RIGHT, padx=5)
# 清除按钮
clear_button = ttk.Button(button_frame, text="清除对话", command=self.clear_conversation)
clear_button.pack(side=tk.RIGHT, padx=5)
# 流式输出选项
self.stream_var = tk.BooleanVar(value=False)
stream_check = ttk.Checkbutton(button_frame, text="流式输出", variable=self.stream_var)
stream_check.pack(side=tk.RIGHT, padx=5)
# 绑定回车键发送消息
self.user_input.bind("<Control-Return>", lambda event: self.send_message())
# 保存对话历史
self.conversation_history = [
{"role": "system", "content": "You are a helpful assistant."}
]
def send_message(self):
user_message = self.user_input.get("1.0", tk.END).strip()
if not user_message:
messagebox.showwarning("输入错误", "请输入消息内容")
return
# 禁用发送按钮防止重复点击
self.send_button.config(state=tk.DISABLED)
# 添加用户消息到对话历史
self.conversation_history.append({"role": "user", "content": user_message})
# 显示用户消息
self.display_message("用户", user_message)
# 清空输入框
self.user_input.delete("1.0", tk.END)
# 在新线程中调用API
threading.Thread(target=self.call_api, daemon=True).start()
def call_api(self):
try:
# 更新API密钥
self.client.api_key = self.api_key_var.get()
# 调用API
response = self.client.chat.completions.create(
model=self.model_var.get(),
messages=self.conversation_history,
stream=self.stream_var.get()
)
if self.stream_var.get():
# 流式输出处理
assistant_message = ""
for chunk in response:
if chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
assistant_message += content
# 实时更新显示(需要在主线程中执行)
self.root.after(0, self.update_stream_display, content)
# 添加完整的助手消息到历史
self.conversation_history.append({"role": "assistant", "content": assistant_message})
else:
# 非流式输出
assistant_message = response.choices[0].message.content
self.conversation_history.append({"role": "assistant", "content": assistant_message})
# 在主线程中更新UI
self.root.after(0, self.display_message, "助手", assistant_message)
except Exception as e:
error_msg = f"API调用错误: {str(e)}"
self.root.after(0, self.display_message, "系统", error_msg, is_error=True)
finally:
# 重新启用发送按钮
self.root.after(0, lambda: self.send_button.config(state=tk.NORMAL))
def update_stream_display(self, content):
# 更新流式输出显示
self.conversation_text.config(state=tk.NORMAL)
self.conversation_text.insert(tk.END, content)
self.conversation_text.see(tk.END)
self.conversation_text.config(state=tk.DISABLED)
def display_message(self, sender, message, is_error=False):
self.conversation_text.config(state=tk.NORMAL)
# 添加发送者标签
if is_error:
self.conversation_text.insert(tk.END, f"{sender}: ", "error")
else:
self.conversation_text.insert(tk.END, f"{sender}: ", "sender")
# 添加消息内容
self.conversation_text.insert(tk.END, f"{message}\n\n")
# 自动滚动到底部
self.conversation_text.see(tk.END)
self.conversation_text.config(state=tk.DISABLED)
# 配置标签样式
self.conversation_text.tag_configure("sender", foreground="blue")
self.conversation_text.tag_configure("error", foreground="red")
def clear_conversation(self):
# 清除对话历史和显示
self.conversation_history = [
{"role": "system", "content": "You are a helpful assistant."}
]
self.conversation_text.config(state=tk.NORMAL)
self.conversation_text.delete("1.0", tk.END)
self.conversation_text.config(state=tk.DISABLED)
if __name__ == "__main__":
root = tk.Tk()
app = DeepSeekChatGUI(root)
root.mainloop()

对应html页面代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DeepSeek Chat Assistant</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f0f0f0;
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 20px;
display: flex;
flex-direction: column;
gap: 20px;
}
/* 顶部配置区样式 */
.config-section {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: center;
padding-bottom: 15px;
border-bottom: 1px solid #eee;
}
.config-group {
display: flex;
flex-direction: column;
gap: 5px;
flex: 1;
min-width: 250px;
}
.config-group label {
font-weight: 500;
color: #333;
font-size: 14px;
}
.config-group input,
.config-group select {
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.config-group input:focus,
.config-group select:focus {
outline: none;
border-color: #4285f4;
box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.1);
}
/* 对话历史区样式 */
.conversation-section {
display: flex;
flex-direction: column;
gap: 8px;
flex: 1;
min-height: 400px;
}
.conversation-section label {
font-weight: 500;
color: #333;
font-size: 14px;
}
#conversation-history {
flex: 1;
padding: 15px;
border: 1px solid #ddd;
border-radius: 6px;
background-color: #fafafa;
overflow-y: auto;
white-space: pre-wrap;
line-height: 1.6;
font-size: 15px;
}
.sender-tag {
font-weight: 600;
color: #4285f4;
}
.error-tag {
font-weight: 600;
color: #ea4335;
}
/* 用户输入区样式 */
.input-section {
display: flex;
flex-direction: column;
gap: 8px;
}
.input-section label {
font-weight: 500;
color: #333;
font-size: 14px;
}
#user-input {
padding: 15px;
border: 1px solid #ddd;
border-radius: 6px;
resize: none;
min-height: 120px;
font-size: 15px;
line-height: 1.6;
transition: border-color 0.3s;
}
#user-input:focus {
outline: none;
border-color: #4285f4;
box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.1);
}
/* 按钮区样式 */
.button-section {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 12px;
padding-top: 10px;
}
.stream-option {
display: flex;
align-items: center;
gap: 5px;
font-size: 14px;
color: #555;
}
button {
padding: 10px 20px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
}
#send-btn {
background-color: #4285f4;
color: white;
}
#send-btn:disabled {
background-color: #a8c7fa;
cursor: not-allowed;
}
#clear-btn {
background-color: #f8f9fa;
color: #333;
border: 1px solid #ddd;
}
#clear-btn:hover {
background-color: #f1f3f4;
}
/* 响应式调整 */
@media (max-width: 768px) {
.config-section {
flex-direction: column;
align-items: stretch;
}
.button-section {
flex-wrap: wrap;
justify-content: space-between;
}
}
</style>
</head>
<body>
<div class="container">
<!-- 顶部配置区:API密钥 + 模型选择 -->
<div class="config-section">
<div class="config-group">
<label for="api-key">API密钥</label>
<input
type="password"
id="api-key"
value="sk-xxxxxxxxxx"
placeholder="请输入DeepSeek API密钥"
>
</div>
<div class="config-group">
<label for="model-select">模型选择</label>
<select id="model-select">
<option value="deepseek-chat" selected>deepseek-chat</option>
<option value="deepseek-coder">deepseek-coder</option>
</select>
</div>
</div>
<!-- 对话历史显示区 -->
<div class="conversation-section">
<label>对话历史</label>
<div id="conversation-history"></div>
</div>
<!-- 用户输入区 -->
<div class="input-section">
<label>你的消息(按 Ctrl+Enter 发送)</label>
<textarea id="user-input" placeholder="请输入你的问题或需求..."></textarea>
</div>
<!-- 按钮区:流式输出选项 + 清除按钮 + 发送按钮 -->
<div class="button-section">
<div class="stream-option">
<input type="checkbox" id="stream-mode">
<label for="stream-mode">流式输出</label>
</div>
<button id="clear-btn">清除对话</button>
<button id="send-btn">发送</button>
</div>
</div>
<script>
// 全局状态管理
const state = {
conversationHistory: [
{ role: "system", content: "You are a helpful assistant." }
],
isSending: false // 防止重复发送
};
// DOM 元素获取
const apiKeyInput = document.getElementById('api-key');
const modelSelect = document.getElementById('model-select');
const conversationHistoryEl = document.getElementById('conversation-history');
const userInputEl = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
const clearBtn = document.getElementById('clear-btn');
const streamModeEl = document.getElementById('stream-mode');
// 初始化事件监听
function initEventListeners() {
// 发送按钮点击事件
sendBtn.addEventListener('click', sendMessage);
// 清除对话按钮点击事件
clearBtn.addEventListener('click', clearConversation);
// Ctrl+Enter 发送消息
userInputEl.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
sendMessage();
}
});
}
// 发送消息处理
async function sendMessage() {
const userMessage = userInputEl.value.trim();
// 输入验证
if (!userMessage) {
alert('请输入消息内容');
return;
}
// 防止重复发送
if (state.isSending) return;
// 更新状态和UI
state.isSending = true;
sendBtn.disabled = true;
sendBtn.textContent = '发送中...';
// 添加用户消息到历史
state.conversationHistory.push({ role: 'user', content: userMessage });
// 显示用户消息
displayMessage('用户', userMessage);
// 清空输入框
userInputEl.value = '';
try {
await callDeepSeekAPI(userMessage);
} catch (error) {
displayMessage('系统', `API调用错误: ${error.message}`, true);
} finally {
// 恢复状态和UI
state.isSending = false;
sendBtn.disabled = false;
sendBtn.textContent = '发送';
}
}
// 调用DeepSeek API
async function callDeepSeekAPI(userMessage) {
const apiKey = apiKeyInput.value.trim();
const model = modelSelect.value;
const streamMode = streamModeEl.checked;
const apiUrl = 'https://api.deepseek.com/v1/chat/completions';
// 请求配置
const requestOptions = {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model,
messages: state.conversationHistory,
stream: streamMode,
temperature: 0.7 // 默认温度值,可调整
})
};
const response = await fetch(apiUrl, requestOptions);
// 处理响应错误
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error?.message || `HTTP错误: ${response.status}`);
}
// 处理流式响应
if (streamMode) {
await handleStreamResponse(response);
} else {
// 处理非流式响应
const responseData = await response.json();
const assistantMessage = responseData.choices[0].message.content;
// 添加到历史
state.conversationHistory.push({ role: 'assistant', content: assistantMessage });
// 显示助手消息
displayMessage('助手', assistantMessage);
}
}
// 处理流式响应
async function handleStreamResponse(response) {
if (!response.body) {
throw new Error('流式响应不支持');
}
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let assistantMessage = '';
let streamComplete = false;
// 显示助手消息前缀(流式输出时先占位)
const assistantPrefixEl = document.createElement('div');
assistantPrefixEl.innerHTML = `<span class="sender-tag">助手:</span>`;
conversationHistoryEl.appendChild(assistantPrefixEl);
scrollToBottom();
try {
while (!streamComplete) {
const { done, value } = await reader.read();
streamComplete = done;
if (value) {
// 解析流式数据(逐行处理)
const chunkText = decoder.decode(value);
const lines = chunkText.split('\n').filter(line => line.trim() !== '');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.substring(6);
if (data === '[DONE]') continue;
try {
const parsed = JSON.parse(data);
const content = parsed.choices[0].delta.content;
if (content) {
assistantMessage += content;
// 更新界面显示
assistantPrefixEl.innerHTML = `<span class="sender-tag">助手:</span>${assistantMessage}`;
scrollToBottom();
}
} catch (parseError) {
console.error('流式数据解析错误:', parseError);
}
}
}
}
}
// 流式结束后添加完整消息到历史
state.conversationHistory.push({ role: 'assistant', content: assistantMessage });
} finally {
reader.releaseLock();
}
}
// 显示消息到对话区域
function displayMessage(sender, message, isError = false) {
const messageEl = document.createElement('div');
const tagClass = isError ? 'error-tag' : 'sender-tag';
// 消息HTML结构(支持换行显示)
messageEl.innerHTML = `
<div style="margin-bottom: 12px;">
<span class="${tagClass}">${sender}:</span>
<span>${message.replace(/\n/g, '<br>')}</span>
</div>
`;
conversationHistoryEl.appendChild(messageEl);
scrollToBottom();
}
// 清除对话历史
function clearConversation() {
// 重置对话历史(保留系统提示)
state.conversationHistory = [
{ role: "system", content: "You are a helpful assistant." }
];
// 清空显示区域
conversationHistoryEl.innerHTML = '';
}
// 滚动到对话底部
function scrollToBottom() {
conversationHistoryEl.scrollTop = conversationHistoryEl.scrollHeight;
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
initEventListeners();
});
</script>
</body>
</html>

四、DeepSeek+tkinter(三)
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
from openai import OpenAI
import threading
from datetime import datetime
class DeepSeekAssistant:
def __init__(self, root):
self.root = root
self.root.title("DeepSeek 智能助手")
self.root.geometry("800x600")
self.root.configure(bg="#f0f0f0")
# API 配置
self.api_key = "sk-xxxxxxxxxx"
self.base_url = "https://api.deepseek.com"
self.model = "deepseek-chat"
self.stream = False
# 对话历史
self.messages = []
self.setup_ui()
def setup_ui(self):
# 主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 配置列权重
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(1, weight=1)
main_frame.rowconfigure(1, weight=1)
# 设置区域
settings_frame = ttk.LabelFrame(main_frame, text="设置", padding="5")
settings_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
# API Key 输入
ttk.Label(settings_frame, text="API Key:").grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
self.api_key_var = tk.StringVar(value=self.api_key)
api_key_entry = ttk.Entry(settings_frame, textvariable=self.api_key_var, width=50, show="*")
api_key_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
# Base URL 输入
ttk.Label(settings_frame, text="Base URL:").grid(row=1, column=0, sticky=tk.W, padx=(0, 5))
self.base_url_var = tk.StringVar(value=self.base_url)
base_url_entry = ttk.Entry(settings_frame, textvariable=self.base_url_var, width=50)
base_url_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
# 模型选择
ttk.Label(settings_frame, text="模型:").grid(row=0, column=2, sticky=tk.W, padx=(10, 5))
self.model_var = tk.StringVar(value=self.model)
model_combo = ttk.Combobox(settings_frame, textvariable=self.model_var,
values=["deepseek-chat", "deepseek-coder"], width=15)
model_combo.grid(row=0, column=3, sticky=tk.W, padx=(0, 10))
# 流式输出选项
self.stream_var = tk.BooleanVar(value=self.stream)
stream_check = ttk.Checkbutton(settings_frame, text="启用流式输出", variable=self.stream_var)
stream_check.grid(row=1, column=2, columnspan=2, sticky=tk.W, padx=(10, 0))
# 保存设置按钮
save_btn = ttk.Button(settings_frame, text="保存设置", command=self.save_settings)
save_btn.grid(row=0, column=4, rowspan=2, padx=(10, 0))
# 聊天显示区域
chat_frame = ttk.LabelFrame(main_frame, text="对话", padding="5")
chat_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
chat_frame.columnconfigure(0, weight=1)
chat_frame.rowconfigure(0, weight=1)
self.chat_display = scrolledtext.ScrolledText(chat_frame, wrap=tk.WORD, width=70, height=20)
self.chat_display.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
self.chat_display.config(state=tk.DISABLED)
# 输入区域
input_frame = ttk.Frame(main_frame)
input_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
input_frame.columnconfigure(0, weight=1)
ttk.Label(input_frame, text="输入消息:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5))
self.input_var = tk.StringVar()
input_entry = ttk.Entry(input_frame, textvariable=self.input_var, width=70)
input_entry.grid(row=1, column=0, sticky=(tk.W, tk.E))
input_entry.bind('<Return>', lambda event: self.send_message())
# 按钮区域
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=3, column=0, columnspan=2, sticky=(tk.W, tk.E))
send_btn = ttk.Button(button_frame, text="发送", command=self.send_message)
send_btn.pack(side=tk.LEFT, padx=(0, 10))
clear_btn = ttk.Button(button_frame, text="清空对话", command=self.clear_chat)
clear_btn.pack(side=tk.LEFT)
# 状态栏
self.status_var = tk.StringVar(value="就绪")
status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
status_bar.grid(row=1, column=0, sticky=(tk.W, tk.E))
def save_settings(self):
self.api_key = self.api_key_var.get()
self.base_url = self.base_url_var.get()
self.model = self.model_var.get()
self.stream = self.stream_var.get()
self.status_var.set("设置已保存")
def send_message(self):
message = self.input_var.get().strip()
if not message:
return
self.input_var.set("") # 清空输入框
self.add_message("user", message)
# 在新线程中调用API,避免界面冻结
thread = threading.Thread(target=self.call_api, args=(message,))
thread.daemon = True
thread.start()
def call_api(self, message):
self.status_var.set("正在生成响应...")
try:
client = OpenAI(api_key=self.api_key, base_url=self.base_url)
# 构建消息
messages = [{"role": "system", "content": "You are a helpful assistant."}]
for msg in self.messages:
messages.append({"role": msg["role"], "content": msg["content"]})
if self.stream:
# 流式响应
stream = client.chat.completions.create(
model=self.model,
messages=messages,
stream=True
)
full_response = ""
for chunk in stream:
if chunk.choices[0].delta.content is not None:
chunk_content = chunk.choices[0].delta.content
full_response += chunk_content
# 实时更新显示
self.root.after(0, self.update_stream_display, chunk_content)
# 添加完整响应到历史
self.add_message("assistant", full_response)
else:
# 非流式响应
response = client.chat.completions.create(
model=self.model,
messages=messages,
stream=False
)
assistant_response = response.choices[0].message.content
self.add_message("assistant", assistant_response)
self.status_var.set("响应完成")
except Exception as e:
error_msg = f"API调用出错: {str(e)}"
self.root.after(0, lambda: messagebox.showerror("错误", error_msg))
self.status_var.set("错误发生")
def add_message(self, role, content):
timestamp = datetime.now().strftime("%H:%M:%S")
self.messages.append({"role": role, "content": content, "time": timestamp})
# 更新显示
self.update_chat_display()
def update_chat_display(self):
self.chat_display.config(state=tk.NORMAL)
self.chat_display.delete(1.0, tk.END)
for msg in self.messages:
role_icon = "👤" if msg["role"] == "user" else "🤖"
role_name = "您" if msg["role"] == "user" else "助手"
self.chat_display.insert(tk.END, f"{role_icon} {role_name} ({msg['time']}):\n")
self.chat_display.insert(tk.END, f"{msg['content']}\n\n")
self.chat_display.see(tk.END)
self.chat_display.config(state=tk.DISABLED)
def update_stream_display(self, chunk):
self.chat_display.config(state=tk.NORMAL)
self.chat_display.insert(tk.END, chunk)
self.chat_display.see(tk.END)
self.chat_display.config(state=tk.DISABLED)
def clear_chat(self):
self.messages = []
self.update_chat_display()
self.status_var.set("对话已清空")
if __name__ == "__main__":
root = tk.Tk()
app = DeepSeekAssistant(root)
root.mainloop()

对应html页面代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DeepSeek 智能助手</title>
<style>
:root {
--primary-color: #4a6cf7;
--secondary-color: #6c757d;
--success-color: #28a745;
--danger-color: #dc3545;
--dark-color: #212529;
--light-color: #f8f9fa;
--sidebar-bg: #2c3e50;
--chat-user-bg: #4a6cf7;
--chat-assistant-bg: #e9ecef;
--chat-user-text: white;
--chat-assistant-text: #212529;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f5f7fb;
color: #333;
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
background: linear-gradient(135deg, var(--primary-color), #3b5bdb);
color: white;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.logo {
display: flex;
align-items: center;
gap: 10px;
}
.logo h1 {
font-size: 1.5rem;
font-weight: 600;
}
.logo-icon {
font-size: 1.8rem;
}
.main-container {
display: flex;
flex: 1;
overflow: hidden;
}
.sidebar {
width: 300px;
background-color: var(--sidebar-bg);
color: white;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
overflow-y: auto;
}
.settings-section {
background-color: rgba(255, 255, 255, 0.1);
padding: 1rem;
border-radius: 8px;
}
.settings-section h3 {
margin-bottom: 1rem;
font-size: 1.1rem;
display: flex;
align-items: center;
gap: 8px;
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-size: 0.9rem;
}
.form-control {
width: 100%;
padding: 0.5rem;
border: none;
border-radius: 4px;
background-color: rgba(255, 255, 255, 0.9);
}
.checkbox-group {
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn {
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-primary:hover {
background-color: #3b5bdb;
}
.btn-danger {
background-color: var(--danger-color);
color: white;
}
.btn-danger:hover {
background-color: #bd2130;
}
.chat-container {
flex: 1;
display: flex;
flex-direction: column;
background-color: white;
}
.chat-messages {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1rem;
}
.message {
max-width: 80%;
padding: 1rem;
border-radius: 12px;
position: relative;
animation: fadeIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.message-user {
align-self: flex-end;
background-color: var(--chat-user-bg);
color: var(--chat-user-text);
border-bottom-right-radius: 4px;
}
.message-assistant {
align-self: flex-start;
background-color: var(--chat-assistant-bg);
color: var(--chat-assistant-text);
border-bottom-left-radius: 4px;
}
.message-header {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
font-size: 0.8rem;
opacity: 0.8;
}
.message-content {
line-height: 1.5;
}
.input-area {
padding: 1rem;
border-top: 1px solid #e9ecef;
background-color: white;
display: flex;
gap: 0.5rem;
}
.message-input {
flex: 1;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 24px;
resize: none;
height: 50px;
outline: none;
transition: border-color 0.2s;
}
.message-input:focus {
border-color: var(--primary-color);
}
.send-btn {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background-color 0.2s;
}
.send-btn:hover {
background-color: #3b5bdb;
}
.send-btn:disabled {
background-color: var(--secondary-color);
cursor: not-allowed;
}
.status-bar {
padding: 0.5rem 1rem;
background-color: #e9ecef;
color: var(--secondary-color);
font-size: 0.85rem;
display: flex;
justify-content: space-between;
}
.typing-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--primary-color);
}
.typing-dots {
display: flex;
gap: 3px;
}
.typing-dot {
width: 6px;
height: 6px;
background-color: var(--primary-color);
border-radius: 50%;
animation: typingAnimation 1.4s infinite ease-in-out;
}
.typing-dot:nth-child(1) { animation-delay: 0s; }
.typing-dot:nth-child(2) { animation-delay: 0.2s; }
.typing-dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes typingAnimation {
0%, 60%, 100% { transform: translateY(0); }
30% { transform: translateY(-5px); }
}
@media (max-width: 768px) {
.main-container {
flex-direction: column;
}
.sidebar {
width: 100%;
max-height: 300px;
}
.message {
max-width: 90%;
}
}
</style>
</head>
<body>
<div class="header">
<div class="logo">
<span class="logo-icon">🤖</span>
<h1>DeepSeek 智能助手</h1>
</div>
</div>
<div class="main-container">
<div class="sidebar">
<div class="settings-section">
<h3>⚙️ API 设置</h3>
<div class="form-group">
<label for="api-key">API Key</label>
<input type="password" id="api-key" class="form-control" value="sk-xxxxxxxxxx">
</div>
<div class="form-group">
<label for="base-url">Base URL</label>
<input type="text" id="base-url" class="form-control" value="https://api.deepseek.com">
</div>
<div class="form-group">
<label for="model">模型</label>
<select id="model" class="form-control">
<option value="deepseek-chat" selected>deepseek-chat</option>
<option value="deepseek-coder">deepseek-coder</option>
</select>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="stream-mode">
<label for="stream-mode">启用流式输出</label>
</div>
</div>
<button id="save-settings" class="btn btn-primary">保存设置</button>
</div>
<div class="settings-section">
<h3>🔄 对话管理</h3>
<button id="clear-chat" class="btn btn-danger">清空对话历史</button>
</div>
</div>
<div class="chat-container">
<div class="chat-messages" id="chat-messages">
<div class="message message-assistant">
<div class="message-header">
<span>🤖 助手</span>
<span>刚刚</span>
</div>
<div class="message-content">
您好!我是DeepSeek智能助手,有什么可以帮您的吗?
</div>
</div>
</div>
<div class="status-bar">
<div id="status">就绪</div>
<div id="typing-indicator" class="typing-indicator" style="display: none;">
<span>正在输入</span>
<div class="typing-dots">
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
</div>
</div>
</div>
<div class="input-area">
<textarea id="message-input" class="message-input" placeholder="输入消息..." rows="1"></textarea>
<button id="send-btn" class="send-btn">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="22" y1="2" x2="11" y2="13"></line>
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
</svg>
</button>
</div>
</div>
</div>
<script>
// 配置和状态
const config = {
apiKey: "sk-xxxxxxxxxx",
baseUrl: "https://api.deepseek.com",
model: "deepseek-chat",
stream: false
};
let messages = [];
let isGenerating = false;
// DOM 元素
const chatMessages = document.getElementById('chat-messages');
const messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');
const statusEl = document.getElementById('status');
const typingIndicator = document.getElementById('typing-indicator');
const apiKeyInput = document.getElementById('api-key');
const baseUrlInput = document.getElementById('base-url');
const modelSelect = document.getElementById('model');
const streamCheckbox = document.getElementById('stream-mode');
const saveSettingsBtn = document.getElementById('save-settings');
const clearChatBtn = document.getElementById('clear-chat');
// 初始化
function init() {
// 加载保存的设置
const savedConfig = localStorage.getItem('deepseek-config');
if (savedConfig) {
const parsedConfig = JSON.parse(savedConfig);
Object.assign(config, parsedConfig);
// 更新UI
apiKeyInput.value = config.apiKey;
baseUrlInput.value = config.baseUrl;
modelSelect.value = config.model;
streamCheckbox.checked = config.stream;
}
// 事件监听
sendBtn.addEventListener('click', sendMessage);
messageInput.addEventListener('keydown', e => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
saveSettingsBtn.addEventListener('click', saveSettings);
clearChatBtn.addEventListener('click', clearChat);
// 自动调整文本区域高度
messageInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
});
}
// 保存设置
function saveSettings() {
config.apiKey = apiKeyInput.value;
config.baseUrl = baseUrlInput.value;
config.model = modelSelect.value;
config.stream = streamCheckbox.checked;
localStorage.setItem('deepseek-config', JSON.stringify(config));
updateStatus('设置已保存');
// 显示成功提示
showTempMessage('设置已成功保存!', 'success');
}
// 清空聊天
function clearChat() {
if (confirm('确定要清空所有对话记录吗?')) {
messages = [];
chatMessages.innerHTML = '';
// 添加欢迎消息
addMessage('assistant', '您好!我是DeepSeek智能助手,有什么可以帮您的吗?');
updateStatus('对话已清空');
}
}
// 发送消息
function sendMessage() {
const message = messageInput.value.trim();
if (!message || isGenerating) return;
// 添加用户消息
addMessage('user', message);
messageInput.value = '';
messageInput.style.height = 'auto';
// 更新状态
isGenerating = true;
sendBtn.disabled = true;
updateStatus('正在生成响应...');
showTypingIndicator(true);
// 调用API
callAPI(message);
}
// 调用API
async function callAPI(message) {
try {
// 构建请求消息
const requestMessages = [
{ role: "system", content: "You are a helpful assistant." },
...messages.map(msg => ({ role: msg.role, content: msg.content }))
];
const requestBody = {
model: config.model,
messages: requestMessages,
stream: config.stream
};
const response = await fetch(config.baseUrl + '/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${config.apiKey}`
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
}
if (config.stream) {
// 流式响应处理
await processStreamResponse(response);
} else {
// 非流式响应处理
await processJsonResponse(response);
}
updateStatus('响应完成');
} catch (error) {
console.error('API调用错误:', error);
addMessage('assistant', `抱歉,发生了错误: ${error.message}`);
updateStatus(`错误: ${error.message}`);
} finally {
isGenerating = false;
sendBtn.disabled = false;
showTypingIndicator(false);
}
}
// 处理流式响应
async function processStreamResponse(response) {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let assistantMessage = '';
let messageElement = null;
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(line => line.trim() !== '');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') {
// 流式响应结束
messages.push({ role: 'assistant', content: assistantMessage });
return;
}
try {
const parsed = JSON.parse(data);
const content = parsed.choices[0]?.delta?.content || '';
if (content) {
assistantMessage += content;
if (!messageElement) {
// 创建新消息元素
messageElement = createMessageElement('assistant', assistantMessage);
chatMessages.appendChild(messageElement);
scrollToBottom();
} else {
// 更新现有消息元素
const contentElement = messageElement.querySelector('.message-content');
contentElement.textContent = assistantMessage;
scrollToBottom();
}
}
} catch (e) {
console.error('解析流数据错误:', e);
}
}
}
}
}
// 处理JSON响应
async function processJsonResponse(response) {
const data = await response.json();
const assistantMessage = data.choices[0].message.content;
addMessage('assistant', assistantMessage);
}
// 添加消息到聊天
function addMessage(role, content) {
const timestamp = new Date().toLocaleTimeString();
messages.push({ role, content, timestamp });
const messageElement = createMessageElement(role, content, timestamp);
chatMessages.appendChild(messageElement);
scrollToBottom();
}
// 创建消息元素
function createMessageElement(role, content, timestamp = null) {
if (!timestamp) {
timestamp = new Date().toLocaleTimeString();
}
const messageDiv = document.createElement('div');
messageDiv.className = `message message-${role}`;
const headerDiv = document.createElement('div');
headerDiv.className = 'message-header';
const roleSpan = document.createElement('span');
roleSpan.textContent = role === 'user' ? '👤 您' : '🤖 助手';
const timeSpan = document.createElement('span');
timeSpan.textContent = timestamp;
headerDiv.appendChild(roleSpan);
headerDiv.appendChild(timeSpan);
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentDiv.textContent = content;
messageDiv.appendChild(headerDiv);
messageDiv.appendChild(contentDiv);
return messageDiv;
}
// 滚动到底部
function scrollToBottom() {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// 更新状态
function updateStatus(text) {
statusEl.textContent = text;
}
// 显示/隐藏输入指示器
function showTypingIndicator(show) {
typingIndicator.style.display = show ? 'flex' : 'none';
}
// 显示临时消息
function showTempMessage(text, type = 'info') {
const tempMsg = document.createElement('div');
tempMsg.textContent = text;
tempMsg.style.position = 'fixed';
tempMsg.style.bottom = '20px';
tempMsg.style.right = '20px';
tempMsg.style.padding = '10px 20px';
tempMsg.style.borderRadius = '4px';
tempMsg.style.color = 'white';
tempMsg.style.zIndex = '1000';
tempMsg.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
if (type === 'success') {
tempMsg.style.backgroundColor = '#28a745';
} else {
tempMsg.style.backgroundColor = '#6c757d';
}
document.body.appendChild(tempMsg);
setTimeout(() => {
tempMsg.style.opacity = '0';
tempMsg.style.transition = 'opacity 0.5s';
setTimeout(() => document.body.removeChild(tempMsg), 500);
}, 3000);
}
// 初始化应用
init();
</script>
</body>
</html>

更多推荐




所有评论(0)