Spaces:
Sleeping
Sleeping
import gradio as gr | |
import requests | |
import json | |
import uuid | |
import logging | |
import os | |
# 配置日志 | |
logging.basicConfig(level=logging.DEBUG) | |
logger = logging.getLogger(__name__) | |
class FastGPTChat: | |
""" | |
def __init__(self, api_key="", | |
system_prompt="我是一名AI用药咨询顾问,请向我提问有关用药的问题"): | |
self.api_key = api_key | |
self.base_url = "https://api.fastgpt.in/api" | |
self.system_prompt = system_prompt | |
self.headers = { | |
"Authorization": f"Bearer {api_key}", | |
"Content-Type": "application/json" | |
} | |
""" | |
def __init__(self, system_prompt="我是一名AI用药咨询顾问,请向我提问有关用药的问题"): | |
# 从环境变量获取API密钥和基础URL | |
self.api_key = os.environ.get('FASTGPT_API_KEY') | |
self.base_url = os.environ.get('FASTGPT_BASE_URL', 'https://api.fastgpt.in/api') | |
self.system_prompt = system_prompt | |
self.headers = { | |
"Authorization": f"Bearer {self.api_key}", | |
"Content-Type": "application/json" | |
} | |
def chat(self, message, chat_history): | |
if not message.strip(): | |
return "", chat_history | |
chat_id = str(uuid.uuid4()) | |
# 构建消息历史,包含系统提示词 | |
messages = [] | |
if self.system_prompt: | |
messages.append({ | |
"role": "system", | |
"content": self.system_prompt | |
}) | |
# 添加聊天历史 | |
for user_msg, bot_msg in chat_history: | |
messages.append({"role": "user", "content": user_msg}) | |
messages.append({"role": "assistant", "content": bot_msg}) | |
# 添加当前用户消息 | |
messages.append({"role": "user", "content": message}) | |
# 准备请求数据 | |
data = { | |
"chatId": chat_id, | |
"stream": True, | |
"detail": True, | |
"model": "gpt-4o", | |
"messages": messages | |
} | |
try: | |
# 发送流式请求 | |
response = requests.post( | |
f"{self.base_url}/v1/chat/completions", | |
headers=self.headers, | |
json=data, | |
stream=True, | |
timeout=30 | |
) | |
if response.status_code != 200: | |
error_msg = f"API Error: Status {response.status_code}" | |
logger.error(error_msg) | |
chat_history.append((message, f"Error: {error_msg}")) | |
return "", chat_history | |
# 处理流式响应 | |
full_response = "" | |
for line in response.iter_lines(): | |
if line: | |
try: | |
line = line.decode('utf-8') | |
if line.startswith('data: '): | |
data = json.loads(line[6:]) | |
if 'choices' in data and len(data['choices']) > 0: | |
content = data['choices'][0]['delta'].get('content', '') | |
if content: | |
full_response += content | |
# 更新聊天历史 | |
if len(chat_history) > 0 and chat_history[-1][0] == message: | |
chat_history[-1] = (message, full_response) | |
else: | |
chat_history.append((message, full_response)) | |
yield "", chat_history | |
except Exception as e: | |
logger.error(f"Error processing stream: {str(e)}") | |
return "", chat_history | |
except requests.exceptions.RequestException as e: | |
error_msg = f"Request failed: {str(e)}" | |
logger.error(error_msg) | |
chat_history.append((message, f"Error: {error_msg}")) | |
return "", chat_history | |
def create_chat_interface(): | |
fastgpt_chat = FastGPTChat() | |
with gr.Blocks( | |
title="AI用药咨询助手", | |
css=""" | |
.container { max-width: 800px; margin: auto; } | |
.chat-header { | |
text-align: center; | |
padding: 20px; | |
background: linear-gradient(135deg, #00b4d8, #0077b6); | |
color: white; | |
border-radius: 15px 15px 0 0; | |
margin-bottom: 20px; | |
} | |
.chat-container { | |
background-color: #ffffff; | |
border-radius: 15px; | |
padding: 20px; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
border: 1px solid #e0e0e0; | |
} | |
.message-box { | |
border: 2px solid #e0e0e0; | |
border-radius: 10px; | |
background-color: white; | |
transition: all 0.3s ease; | |
} | |
.message-box:focus { | |
border-color: #00b4d8; | |
box-shadow: 0 0 0 2px rgba(0, 180, 216, 0.2); | |
} | |
.submit-btn { | |
background-color: #00b4d8 !important; | |
border: none !important; | |
color: white !important; | |
padding: 10px 20px !important; | |
border-radius: 8px !important; | |
transition: all 0.3s ease !important; | |
font-weight: 600 !important; | |
} | |
.submit-btn:hover { | |
background-color: #0077b6 !important; | |
transform: translateY(-2px); | |
} | |
.clear-btn { | |
background-color: #ff4757 !important; | |
border: none !important; | |
color: white !important; | |
padding: 10px 20px !important; | |
border-radius: 8px !important; | |
transition: all 0.3s ease !important; | |
font-weight: 600 !important; | |
} | |
.clear-btn:hover { | |
background-color: #ff6b81 !important; | |
transform: translateY(-2px); | |
} | |
.chatbot { | |
background-color: #f8f9fa; | |
border-radius: 10px; | |
padding: 15px; | |
margin-bottom: 20px; | |
} | |
/* 移除消息背景的白色遮盖,优化对话样式 */ | |
.chatbot > div { | |
background: transparent !important; | |
} | |
/* 用户消息样式 */ | |
.chatbot .user-message { | |
background-color: #fce4ec !important; | |
border-radius: 15px 15px 2px 15px !important; | |
padding: 12px 18px !important; | |
margin: 8px 0 !important; | |
max-width: 85% !important; | |
float: right !important; | |
clear: both !important; | |
border: 1px solid #f48fb1 !important; | |
color: #1a1a1a !important; | |
font-size: 15px !important; | |
} | |
/* AI响应消息样式 */ | |
.chatbot .bot-message { | |
background-color: #e8f5e9 !important; | |
border-radius: 15px 15px 15px 2px !important; | |
padding: 12px 18px !important; | |
margin: 8px 0 !important; | |
max-width: 85% !important; | |
float: left !important; | |
clear: both !important; | |
border: 1px solid #a5d6a7 !important; | |
color: #1a1a1a !important; | |
font-size: 15px !important; | |
} | |
/* 消息容器样式 */ | |
.chatbot > div > div { | |
padding: 0 !important; | |
gap: 2rem !important; | |
} | |
/* 头像样式 */ | |
.chatbot span.avatar { | |
padding: 8px !important; | |
margin: 8px !important; | |
} | |
/* 确保消息之间有适当的间距 */ | |
.chatbot > div > div:not(:last-child) { | |
margin-bottom: 15px !important; | |
} | |
""" | |
) as interface: | |
with gr.Column(elem_classes="container"): | |
gr.Markdown( | |
""" | |
# 🏥 AI用药咨询助手 | |
### 您的专业用药咨询顾问,随时为您解答用药相关问题 | |
""" | |
) | |
with gr.Column(elem_classes="chat-container"): | |
chatbot = gr.Chatbot( | |
height=500, | |
container=False, | |
elem_classes="chatbot", | |
show_label=False, | |
bubble_full_width=False, | |
avatar_images=("👤", "🤖") | |
) | |
with gr.Row(elem_id="input-container"): | |
with gr.Column(scale=8): | |
message = gr.Textbox( | |
show_label=False, | |
placeholder="请输入您的用药问题...", | |
container=False, | |
elem_classes="message-box", | |
lines=2 | |
) | |
with gr.Column(scale=1, min_width=100): | |
submit = gr.Button( | |
"发送 💊", | |
variant="primary", | |
elem_classes="submit-btn" | |
) | |
with gr.Row(elem_id="control-container"): | |
clear = gr.Button( | |
"清空对话 🗑️", | |
variant="secondary", | |
elem_classes="clear-btn" | |
) | |
# 设置事件处理 | |
submit_click = submit.click( | |
fastgpt_chat.chat, | |
inputs=[message, chatbot], | |
outputs=[message, chatbot], | |
api_name="chat" | |
) | |
message.submit( | |
fastgpt_chat.chat, | |
inputs=[message, chatbot], | |
outputs=[message, chatbot] | |
) | |
clear.click(lambda: None, None, chatbot, queue=False) | |
return interface | |
if __name__ == "__main__": | |
demo = create_chat_interface() | |
demo.launch(debug=True) |