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)