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, system_prompt="我是一名AI用药咨询顾问,请向我提问有关用药的问题"): | |
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_history.append((message, "")) | |
yield "", 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[:-1]: # 不包含最新消息 | |
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[-1] = (message, f"Error: {error_msg}") | |
yield "", chat_history | |
return | |
# 处理流式响应 | |
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 | |
chat_history[-1] = (message, full_response) | |
yield "", chat_history | |
except Exception as e: | |
logger.error(f"Error processing stream: {str(e)}") | |
yield "", chat_history | |
except requests.exceptions.RequestException as e: | |
error_msg = f"Request failed: {str(e)}" | |
logger.error(error_msg) | |
chat_history[-1] = (message, f"Error: {error_msg}") | |
yield "", chat_history | |
def create_chat_interface(): | |
fastgpt_chat = FastGPTChat() | |
with gr.Blocks( | |
title="AI用药咨询助手", | |
css=""" | |
body { | |
background-color: #F4F6F9; | |
font-family: 'Inter', 'PingFang SC', sans-serif; | |
} | |
.container { | |
max-width: 800px; | |
margin: 2rem auto; | |
padding: 1rem; | |
box-sizing: border-box; | |
} | |
.title-container { | |
text-align: center; | |
margin-bottom: 1.5rem; | |
} | |
.title-container h1 { | |
color: #2C3E50; | |
font-size: 2.2rem; | |
font-weight: 700; | |
margin-bottom: 0.5rem; | |
} | |
.title-container h3 { | |
color: #6B7280; | |
font-size: 1rem; | |
font-weight: 400; | |
} | |
.chat-container { | |
background-color: white; | |
border-radius: 16px; | |
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); | |
overflow: hidden; | |
} | |
.chatbot { | |
height: 500px !important; | |
background-color: #F9FAFB; | |
padding: 1rem; | |
overflow-y: auto; | |
} | |
.chatbot .user-message { | |
background: linear-gradient(135deg, #3B82F6, #2563EB); | |
color: white !important; | |
border-radius: 16px 16px 4px 16px !important; | |
align-self: flex-end !important; | |
max-width: 80% !important; | |
} | |
.chatbot .bot-message { | |
background: linear-gradient(135deg, #F3F4F6, #FFFFFF); | |
color: #1F2937 !important; | |
border: 1px solid #E5E7EB !important; | |
border-radius: 16px 16px 16px 4px !important; | |
align-self: flex-start !important; | |
max-width: 80% !important; | |
} | |
.input-container { | |
background: white; | |
padding: 1rem; | |
border-top: 1px solid #E5E7EB; | |
} | |
.message-box { | |
border: 2px solid #E5E7EB !important; | |
border-radius: 12px !important; | |
padding: 0.75rem !important; | |
font-size: 0.95rem !important; | |
transition: all 0.3s ease !important; | |
background: white !important; | |
} | |
.message-box:focus { | |
border-color: #3B82F6 !important; | |
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2) !important; | |
} | |
.button-container { | |
display: flex; | |
gap: 1rem; | |
margin-top: 1rem; | |
} | |
.submit-btn { | |
background: linear-gradient(135deg, #3B82F6, #2563EB) !important; | |
color: white !important; | |
padding: 0.75rem 1.5rem !important; | |
border-radius: 12px !important; | |
font-weight: 600 !important; | |
transition: all 0.3s ease !important; | |
flex: 1; | |
} | |
.submit-btn:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 6px 16px rgba(59, 130, 246, 0.3) !important; | |
} | |
.clear-btn { | |
background: #EF4444 !important; | |
color: white !important; | |
padding: 0.75rem 1.5rem !important; | |
border-radius: 12px !important; | |
font-weight: 600 !important; | |
transition: all 0.3s ease !important; | |
} | |
.clear-btn:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 6px 16px rgba(239, 68, 68, 0.3) !important; | |
} | |
@media (max-width: 600px) { | |
.container { | |
margin: 1rem; | |
padding: 0.5rem; | |
} | |
.chatbot { | |
height: 400px !important; | |
} | |
} | |
""" | |
) as interface: | |
with gr.Column(elem_classes="container"): | |
with gr.Column(elem_classes="title-container"): | |
gr.Markdown( | |
""" | |
# 🏥 AI用药咨询助手 | |
### 您的专业用药咨询顾问,随时为您解答用药相关问题 | |
""" | |
) | |
with gr.Column(elem_classes="chat-container"): | |
chatbot = gr.Chatbot( | |
height=600, | |
container=False, | |
elem_classes="chatbot", | |
show_label=False, | |
bubble_full_width=False, | |
avatar_images=("👤", "🤖") | |
) | |
with gr.Column(elem_classes="input-container"): | |
message = gr.Textbox( | |
show_label=False, | |
placeholder="请输入您的用药问题...", | |
container=False, | |
elem_classes="message-box", | |
lines=1, # 减小输入框初始行数 | |
max_lines=5 # 设置最大行数 | |
) | |
with gr.Row(elem_classes="button-container"): | |
submit = gr.Button( | |
"发送 💊", | |
variant="primary", | |
elem_classes="submit-btn" | |
) | |
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.queue().launch(debug=True, server_name="0.0.0.0") |