From 31581e24a9ae17ef5a17f9119897758899939f63 Mon Sep 17 00:00:00 2001 From: eust-w Date: Tue, 17 Jun 2025 20:14:11 +0800 Subject: [PATCH] add tts --- app.py | 173 +++++++++++++++++++++- app/api/azure_openai.py | 12 +- app/api/robot_manager.py | 217 ++++++++++++++++++++++++++++ app/static/js/chat.js | 86 ++++++++++- app/static/js/robots.js | 297 ++++++++++++++++++++++++++++++++++++++ app/templates/chat.html | 27 +++- app/templates/index.html | 11 +- app/templates/robots.html | 186 ++++++++++++++++++++++++ 8 files changed, 993 insertions(+), 16 deletions(-) create mode 100644 app/api/robot_manager.py create mode 100644 app/static/js/robots.js create mode 100644 app/templates/robots.html diff --git a/app.py b/app.py index 37d83b7..07dc7af 100644 --- a/app.py +++ b/app.py @@ -4,13 +4,15 @@ import os import json import base64 -from flask import Flask, render_template, request, jsonify, redirect, url_for, session +import requests +from flask import Flask, render_template, request, jsonify, redirect, url_for, session, Response from flask_cors import CORS from werkzeug.utils import secure_filename from app.api.baidu_image_search import BaiduImageSearch from app.api.image_utils import ImageUtils from app.api.azure_openai import AzureOpenAI from app.api.type_manager_mongo import TypeManagerMongo +from app.api.robot_manager import RobotManager app = Flask(__name__, template_folder='app/templates', static_folder='app/static') CORS(app) # 启用CORS支持跨域请求 @@ -40,6 +42,9 @@ except ValueError as e: # 初始化类型管理器 type_manager = TypeManagerMongo() +# 初始化机器人角色管理器 +robot_manager = RobotManager() + def allowed_file(filename): """检查文件扩展名是否允许""" return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @@ -231,6 +236,9 @@ def upload_chat_image(): if not allowed_file(file.filename): return jsonify({'error': '不支持的文件类型'}), 400 + # 获取选择的机器人角色ID + robot_id = request.form.get('robot_id', '') + # 保存文件 filename = secure_filename(file.filename) file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) @@ -239,6 +247,9 @@ def upload_chat_image(): # 存储图片路径到会话 session['chat_image_path'] = file_path + # 存储机器人角色ID到会话 + session['robot_id'] = robot_id + # 清空对话历史 session['conversation_history'] = [] @@ -269,11 +280,22 @@ def upload_chat_image(): except: continue + # 获取机器人角色信息 + robot_info = None + if robot_id: + robot = robot_manager.get_robot(robot_id) + if robot: + robot_info = { + 'name': robot.get('name', ''), + 'background': robot.get('background', '') + } + return jsonify({ 'success': True, 'image_path': file_path, 'image_type': image_type, - 'description': description + 'description': description, + 'robot': robot_info }) except Exception as e: return jsonify({'error': str(e)}), 500 @@ -290,6 +312,7 @@ def chat(): message = data['message'] image_path = session.get('chat_image_path') + robot_id = session.get('robot_id', '') if not image_path or not os.path.exists(image_path): return jsonify({'error': '没有上传图片或图片已失效'}), 400 @@ -297,12 +320,23 @@ def chat(): # 获取对话历史 conversation_history = session.get('conversation_history', []) + # 获取机器人角色信息 + robot_info = None + if robot_id: + robot = robot_manager.get_robot(robot_id) + if robot: + robot_info = { + 'name': robot.get('name', ''), + 'background': robot.get('background', '') + } + try: # 调用Azure OpenAI API进行对话 response = azure_openai_api.chat_with_image( image_path=image_path, message=message, - conversation_history=conversation_history + conversation_history=conversation_history, + robot_info=robot_info ) # 提取回复内容 @@ -320,5 +354,138 @@ def chat(): except Exception as e: return jsonify({'error': str(e)}), 500 +@app.route('/imgsearcherApi/robots', methods=['GET']) +def robot_page(): + """机器人角色管理页面""" + return render_template('robots.html') + +@app.route('/imgsearcherApi/api/robots', methods=['GET']) +def get_robots(): + """获取所有机器人角色""" + try: + robots = robot_manager.get_all_robots() + return jsonify({ + 'success': True, + 'robots': robots + }) + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@app.route('/imgsearcherApi/api/robots', methods=['POST']) +def add_robot(): + """添加新的机器人角色""" + try: + name = request.form.get('name') + background = request.form.get('background') + + if not name or not background: + return jsonify({'error': '机器人名称和背景故事不能为空'}), 400 + + avatar_file = None + if 'avatar' in request.files: + avatar_file = request.files['avatar'] + if avatar_file.filename == '': + avatar_file = None + elif not allowed_file(avatar_file.filename): + return jsonify({'error': '不支持的文件类型'}), 400 + + result = robot_manager.add_robot(name, background, avatar_file) + + if 'error' in result: + return jsonify(result), 400 + + return jsonify({ + 'success': True, + 'robot': result + }) + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@app.route('/imgsearcherApi/api/robots/', methods=['GET']) +def get_robot(robot_id): + """获取指定机器人角色""" + try: + robot = robot_manager.get_robot(robot_id) + if not robot: + return jsonify({'error': '找不到该机器人'}), 404 + + return jsonify({ + 'success': True, + 'robot': robot + }) + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@app.route('/imgsearcherApi/api/robots/', methods=['PUT']) +def update_robot(robot_id): + """更新机器人角色""" + try: + name = request.form.get('name') + background = request.form.get('background') + + avatar_file = None + if 'avatar' in request.files: + avatar_file = request.files['avatar'] + if avatar_file.filename == '': + avatar_file = None + elif not allowed_file(avatar_file.filename): + return jsonify({'error': '不支持的文件类型'}), 400 + + result = robot_manager.update_robot(robot_id, name, background, avatar_file) + + if 'error' in result: + return jsonify(result), 400 + + return jsonify({ + 'success': True, + 'robot': result + }) + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@app.route('/imgsearcherApi/api/robots/', methods=['DELETE']) +def delete_robot(robot_id): + """删除机器人角色""" + try: + success = robot_manager.delete_robot(robot_id) + if not success: + return jsonify({'error': '找不到该机器人或删除失败'}), 404 + + return jsonify({ + 'success': True + }) + except Exception as e: + return jsonify({'error': str(e)}), 500 + +# ==================== TTS ==================== +@app.route('/imgsearcherApi/api/tts', methods=['POST']) +def tts(): + """文本转语音接口,返回音频数据流""" + data = request.get_json() + if not data or 'text' not in data: + return jsonify({'error': '缺少text参数'}), 400 + + text = data['text'] + prompt_text = data.get('prompt_text') or "我是威震天,我只代表月亮消灭你" + prompt_wav = data.get('prompt_wav') or "data_workspace/data/workspace_170/我是威震天.wav" + + tts_base_url = "http://180.76.186.85:20099/CosyVoice/v1/zero_shot" + params = { + 'tts_text': text, + 'prompt_text': prompt_text, + 'prompt_wav': prompt_wav, + 'text_split_method': 'cut5' + } + + try: + r = requests.get(tts_base_url, params=params, timeout=20) + if r.status_code == 200: + return Response(r.content, content_type=r.headers.get('Content-Type', 'audio/wav')) + else: + print(f"TTS remote error {r.status_code}: {r.text}") + return jsonify({'error': f'TTS服务错误: {r.text}'}), 502 + except Exception as e: + return jsonify({'error': f'TTS请求失败: {str(e)}'}), 500 + if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5001) diff --git a/app/api/azure_openai.py b/app/api/azure_openai.py index f96a1b4..8bfa8b4 100644 --- a/app/api/azure_openai.py +++ b/app/api/azure_openai.py @@ -85,7 +85,7 @@ class AzureOpenAI: print(f"获取图片类型和描述失败: {str(e)}") return "", "" - def chat_with_image(self, image_path, message, conversation_history=None): + def chat_with_image(self, image_path, message, conversation_history=None, robot_info=None): """ 使用图片和消息与GPT-4o多模态模型进行对话 @@ -93,6 +93,7 @@ class AzureOpenAI: image_path: 图片路径 message: 用户消息 conversation_history: 对话历史记录 + robot_info: 机器人角色信息,包含name和background Returns: dict: 模型响应 @@ -107,7 +108,14 @@ class AzureOpenAI: base64_image = self._encode_image(image_path) # 构建系统提示 - system_message = "你是一个智能助手,能够分析图片并回答问题。" + if robot_info and robot_info.get('name') and robot_info.get('background'): + # 如果有机器人角色信息,使用机器人角色 + system_message = f"你是{robot_info['name']},一个能够分析图片并回答问题的角色。\n\n你的背景故事:{robot_info['background']}\n\n在对话中,你应该始终保持这个角色的身份和特点,用第一人称回答问题。" + else: + # 默认智能助手 + system_message = "你是一个智能助手,能够分析图片并回答问题。" + + # 添加图片类型和描述信息 if image_type and description: system_message += f"\n\n这是一张{image_type}的图片。\n描述信息:{description}\n\n请基于这些信息和图片内容回答用户的问题。" diff --git a/app/api/robot_manager.py b/app/api/robot_manager.py new file mode 100644 index 0000000..35e641a --- /dev/null +++ b/app/api/robot_manager.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import datetime +from pymongo import MongoClient +from dotenv import load_dotenv +import base64 +import uuid + +# 加载环境变量 +load_dotenv() + +class RobotManager: + """管理机器人角色的类""" + + def __init__(self): + """初始化MongoDB连接""" + # 从环境变量获取MongoDB连接信息 + mongo_uri = os.getenv('MONGO_URI', 'mongodb://localhost:27017/') + db_name = os.getenv('MONGO_DB_NAME', 'imgsearcher') + + # 连接MongoDB + self.client = MongoClient(mongo_uri) + self.db = self.client[db_name] + self.collection = self.db['robot_characters'] + + # 确保有索引以提高查询性能 + self.collection.create_index('robot_id') + self.collection.create_index('name') + + # 上传文件夹路径 + self.upload_folder = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'uploads') + if not os.path.exists(self.upload_folder): + os.makedirs(self.upload_folder) + + print(f"MongoDB RobotManager 初始化完成,连接到 {db_name}") + + def add_robot(self, name, background, avatar_file=None): + """ + 添加新的机器人角色 + + Args: + name: 机器人名称 + background: 机器人背景故事 + avatar_file: 机器人头像文件(可选) + + Returns: + dict: 包含新添加的机器人信息 + """ + if not name or not background: + return {"error": "机器人名称和背景故事不能为空"} + + # 检查是否已存在同名机器人 + if self.collection.find_one({"name": name}): + return {"error": f"已存在名为 '{name}' 的机器人"} + + # 生成唯一ID + robot_id = str(uuid.uuid4()) + + # 处理头像文件 + avatar_path = None + if avatar_file: + # 保存头像文件 + filename = f"robot_avatar_{robot_id}.{avatar_file.filename.split('.')[-1]}" + file_path = os.path.join(self.upload_folder, filename) + avatar_file.save(file_path) + avatar_path = file_path + + # 创建机器人记录 + robot = { + "robot_id": robot_id, + "name": name, + "background": background, + "avatar_path": avatar_path, + "created_at": datetime.datetime.now() + } + + # 保存到数据库 + self.collection.insert_one(robot) + + # 返回新添加的机器人信息(不包含MongoDB的_id字段) + robot.pop("_id", None) + return robot + + def get_robot(self, robot_id): + """ + 获取指定ID的机器人信息 + + Args: + robot_id: 机器人ID + + Returns: + dict: 机器人信息,如果不存在则返回None + """ + robot = self.collection.find_one({"robot_id": robot_id}) + if robot: + # 转换MongoDB的_id为字符串 + robot["_id"] = str(robot["_id"]) + + # 如果有头像,添加base64编码的头像数据 + if robot.get("avatar_path") and os.path.exists(robot["avatar_path"]): + with open(robot["avatar_path"], "rb") as f: + avatar_data = base64.b64encode(f.read()).decode('utf-8') + robot["avatar_data"] = avatar_data + + return robot + return None + + def update_robot(self, robot_id, name=None, background=None, avatar_file=None): + """ + 更新机器人信息 + + Args: + robot_id: 机器人ID + name: 新的机器人名称(可选) + background: 新的背景故事(可选) + avatar_file: 新的头像文件(可选) + + Returns: + dict: 更新后的机器人信息 + """ + # 获取现有机器人 + robot = self.collection.find_one({"robot_id": robot_id}) + if not robot: + return {"error": f"找不到ID为 '{robot_id}' 的机器人"} + + # 准备更新数据 + update_data = {} + + if name: + # 检查新名称是否与其他机器人冲突 + existing = self.collection.find_one({"name": name, "robot_id": {"$ne": robot_id}}) + if existing: + return {"error": f"已存在名为 '{name}' 的机器人"} + update_data["name"] = name + + if background: + update_data["background"] = background + + # 处理新头像 + if avatar_file: + # 删除旧头像 + old_avatar_path = robot.get("avatar_path") + if old_avatar_path and os.path.exists(old_avatar_path): + try: + os.remove(old_avatar_path) + except: + pass + + # 保存新头像 + filename = f"robot_avatar_{robot_id}.{avatar_file.filename.split('.')[-1]}" + file_path = os.path.join(self.upload_folder, filename) + avatar_file.save(file_path) + update_data["avatar_path"] = file_path + + # 更新数据库 + if update_data: + update_data["updated_at"] = datetime.datetime.now() + self.collection.update_one({"robot_id": robot_id}, {"$set": update_data}) + + # 返回更新后的机器人信息 + return self.get_robot(robot_id) + + def delete_robot(self, robot_id): + """ + 删除机器人 + + Args: + robot_id: 机器人ID + + Returns: + bool: 是否成功删除 + """ + # 获取机器人信息 + robot = self.collection.find_one({"robot_id": robot_id}) + if not robot: + return False + + # 删除头像文件 + avatar_path = robot.get("avatar_path") + if avatar_path and os.path.exists(avatar_path): + try: + os.remove(avatar_path) + except: + pass + + # 从数据库中删除 + result = self.collection.delete_one({"robot_id": robot_id}) + return result.deleted_count > 0 + + def get_all_robots(self): + """ + 获取所有机器人列表 + + Returns: + list: 机器人信息列表 + """ + robots = [] + for robot in self.collection.find(): + # 转换MongoDB的_id为字符串 + robot["_id"] = str(robot["_id"]) + + # 如果有头像,添加base64编码的头像数据 + if robot.get("avatar_path") and os.path.exists(robot["avatar_path"]): + with open(robot["avatar_path"], "rb") as f: + avatar_data = base64.b64encode(f.read()).decode('utf-8') + robot["avatar_data"] = avatar_data + + robots.append(robot) + + return robots + + def close(self): + """关闭MongoDB连接""" + if self.client: + self.client.close() diff --git a/app/static/js/chat.js b/app/static/js/chat.js index 176a689..963ce62 100644 --- a/app/static/js/chat.js +++ b/app/static/js/chat.js @@ -1,5 +1,8 @@ // 页面加载完成后执行 document.addEventListener('DOMContentLoaded', function() { + // 加载机器人角色列表 + loadRobotCharacters(); + // 初始化图片上传表单 initUploadImageForm(); @@ -7,6 +10,28 @@ document.addEventListener('DOMContentLoaded', function() { initChatForm(); }); +// 加载机器人角色列表 +async function loadRobotCharacters() { + const robotSelect = document.getElementById('robotSelect'); + + try { + const response = await fetch('/imgsearcherApi/api/robots'); + const data = await response.json(); + + if (data.robots && data.robots.length > 0) { + // 添加机器人选项 + data.robots.forEach(robot => { + const option = document.createElement('option'); + option.value = robot.robot_id; + option.textContent = robot.name; + robotSelect.appendChild(option); + }); + } + } catch (error) { + console.error('加载机器人角色失败:', error); + } +} + // 初始化图片上传表单 function initUploadImageForm() { const form = document.getElementById('uploadImageForm'); @@ -74,14 +99,31 @@ function initUploadImageForm() { // 显示聊天界面 chatContainer.classList.remove('d-none'); - // 添加欢迎消息 + // 显示欢迎消息 const chatMessages = document.getElementById('chatMessages'); chatMessages.innerHTML = ''; const welcomeMessage = document.createElement('div'); welcomeMessage.className = 'message message-assistant'; - let welcomeText = '你好!我是AI助手,可以帮你分析这张图片并回答问题。'; + let welcomeText = ''; + + // 如果选择了机器人角色,使用机器人的自我介绍 + if (data.robot) { + welcomeText = `你好!我是${data.robot.name},`; + // 存储机器人信息到页面数据中 + document.getElementById('chatContainer').dataset.robotName = data.robot.name; + + // 显示机器人信息在界面上 + const robotInfoElement = document.createElement('div'); + robotInfoElement.className = 'alert alert-info mt-2 mb-3'; + robotInfoElement.innerHTML = `当前角色:${data.robot.name}
${data.robot.background}`; + document.getElementById('chatContainer').prepend(robotInfoElement); + } else { + welcomeText = '你好!我是AI助手,'; + } + + welcomeText += '可以帮你分析这张图片并回答问题。'; if (data.image_type && data.description) { welcomeText += `我看到这是一张${data.image_type}的图片,${data.description}。你有什么想问的吗?`; } else { @@ -171,7 +213,7 @@ function initChatForm() { } // 添加消息到聊天界面 -function addMessage(role, content) { +async function addMessage(role, content) { const chatMessages = document.getElementById('chatMessages'); const messageElement = document.createElement('div'); @@ -183,6 +225,44 @@ function addMessage(role, content) { `; chatMessages.appendChild(messageElement); + + // 如果是AI回复,调用TTS并播放 + if (role === 'assistant') { + try { + const ttsResp = await fetch('/imgsearcherApi/api/tts', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + text: content + }) + }); + if (ttsResp.ok) { + const blob = await ttsResp.blob(); + const audioUrl = URL.createObjectURL(blob); + const audio = document.createElement('audio'); + audio.src = audioUrl; + audio.autoplay = true; + // 可选:提供播放控制按钮 + const controls = document.createElement('div'); + controls.className = 'audio-controls mt-1'; + const playBtn = document.createElement('button'); + playBtn.type = 'button'; + playBtn.className = 'btn btn-sm btn-outline-secondary'; + playBtn.textContent = '🔊 播放'; + playBtn.addEventListener('click', () => { + audio.play(); + }); + controls.appendChild(playBtn); + messageElement.appendChild(controls); + } else { + console.error('TTS 接口错误', await ttsResp.text()); + } + } catch (err) { + console.error('TTS 播放失败', err); + } + } // 滚动到底部 chatMessages.scrollTop = chatMessages.scrollHeight; diff --git a/app/static/js/robots.js b/app/static/js/robots.js new file mode 100644 index 0000000..71ef84f --- /dev/null +++ b/app/static/js/robots.js @@ -0,0 +1,297 @@ +// 页面加载完成后执行 +document.addEventListener('DOMContentLoaded', function() { + // 初始化机器人表单 + initRobotForm(); + + // 加载机器人列表 + loadRobots(); + + // 初始化编辑和删除功能 + initEditRobotModal(); + initDeleteRobotModal(); +}); + +// 初始化机器人表单 +function initRobotForm() { + const form = document.getElementById('robotForm'); + const avatarInput = document.getElementById('robotAvatar'); + const avatarPreview = document.getElementById('avatarPreview'); + const avatarPreviewContainer = document.querySelector('#robotForm .avatar-preview'); + + // 头像预览 + avatarInput.addEventListener('change', function() { + if (this.files && this.files[0]) { + const reader = new FileReader(); + + reader.onload = function(e) { + avatarPreview.src = e.target.result; + avatarPreviewContainer.classList.remove('d-none'); + }; + + reader.readAsDataURL(this.files[0]); + } else { + avatarPreviewContainer.classList.add('d-none'); + } + }); + + // 表单提交 + form.addEventListener('submit', async function(e) { + e.preventDefault(); + + const formData = new FormData(this); + + // 显示提交中状态 + const submitButton = form.querySelector('button[type="submit"]'); + const originalButtonText = submitButton.innerHTML; + submitButton.disabled = true; + submitButton.innerHTML = ' 添加中...'; + + try { + const response = await fetch('/imgsearcherApi/api/robots', { + method: 'POST', + body: formData + }); + + const data = await response.json(); + + if (data.error) { + alert(`添加失败: ${data.error}`); + return; + } + + // 重置表单 + form.reset(); + avatarPreviewContainer.classList.add('d-none'); + + // 刷新机器人列表 + loadRobots(); + + // 显示成功消息 + alert('机器人角色添加成功!'); + + } catch (error) { + alert(`添加失败: ${error.message}`); + } finally { + // 恢复按钮状态 + submitButton.disabled = false; + submitButton.innerHTML = originalButtonText; + } + }); +} + +// 加载机器人列表 +async function loadRobots() { + const robotsList = document.getElementById('robotsList'); + const loadingIndicator = document.getElementById('loadingIndicator'); + const noRobotsMessage = document.getElementById('noRobotsMessage'); + + // 显示加载指示器 + loadingIndicator.classList.remove('d-none'); + noRobotsMessage.classList.add('d-none'); + + // 清除现有的机器人卡片(保留标题和加载指示器) + const existingCards = robotsList.querySelectorAll('.robot-card-container'); + existingCards.forEach(card => card.remove()); + + try { + const response = await fetch('/imgsearcherApi/api/robots'); + const data = await response.json(); + + // 隐藏加载指示器 + loadingIndicator.classList.add('d-none'); + + if (!data.robots || data.robots.length === 0) { + noRobotsMessage.classList.remove('d-none'); + return; + } + + // 渲染机器人卡片 + data.robots.forEach(robot => { + const robotCard = createRobotCard(robot); + robotsList.appendChild(robotCard); + }); + + } catch (error) { + console.error('加载机器人列表失败:', error); + loadingIndicator.classList.add('d-none'); + alert(`加载机器人列表失败: ${error.message}`); + } +} + +// 创建机器人卡片 +function createRobotCard(robot) { + const cardContainer = document.createElement('div'); + cardContainer.className = 'col-md-4 mb-4 robot-card-container'; + + const avatarHtml = robot.avatar_data + ? `${robot.name}` + : `
`; + + cardContainer.innerHTML = ` +
+
+ ${avatarHtml} +
${robot.name}
+
+ ${robot.background} +
+
+ + +
+
+
+ `; + + // 添加编辑按钮事件 + cardContainer.querySelector('.edit-robot').addEventListener('click', function() { + openEditRobotModal(robot); + }); + + // 添加删除按钮事件 + cardContainer.querySelector('.delete-robot').addEventListener('click', function() { + openDeleteRobotModal(robot.robot_id, robot.name); + }); + + return cardContainer; +} + +// 初始化编辑机器人模态框 +function initEditRobotModal() { + const editRobotForm = document.getElementById('editRobotForm'); + const saveChangesButton = document.getElementById('saveRobotChanges'); + const editRobotAvatar = document.getElementById('editRobotAvatar'); + const editAvatarPreview = document.getElementById('editAvatarPreview'); + + // 头像预览 + editRobotAvatar.addEventListener('change', function() { + if (this.files && this.files[0]) { + const reader = new FileReader(); + + reader.onload = function(e) { + editAvatarPreview.src = e.target.result; + }; + + reader.readAsDataURL(this.files[0]); + } + }); + + // 保存更改按钮点击事件 + saveChangesButton.addEventListener('click', async function() { + const robotId = document.getElementById('editRobotId').value; + const formData = new FormData(editRobotForm); + + // 显示保存中状态 + const originalButtonText = saveChangesButton.innerHTML; + saveChangesButton.disabled = true; + saveChangesButton.innerHTML = ' 保存中...'; + + try { + const response = await fetch(`/imgsearcherApi/api/robots/${robotId}`, { + method: 'PUT', + body: formData + }); + + const data = await response.json(); + + if (data.error) { + alert(`更新失败: ${data.error}`); + return; + } + + // 关闭模态框 + const editModal = bootstrap.Modal.getInstance(document.getElementById('editRobotModal')); + editModal.hide(); + + // 刷新机器人列表 + loadRobots(); + + // 显示成功消息 + alert('机器人角色更新成功!'); + + } catch (error) { + alert(`更新失败: ${error.message}`); + } finally { + // 恢复按钮状态 + saveChangesButton.disabled = false; + saveChangesButton.innerHTML = originalButtonText; + } + }); +} + +// 打开编辑机器人模态框 +function openEditRobotModal(robot) { + document.getElementById('editRobotId').value = robot.robot_id; + document.getElementById('editRobotName').value = robot.name; + document.getElementById('editRobotBackground').value = robot.background; + + // 设置头像预览 + if (robot.avatar_data) { + document.getElementById('editAvatarPreview').src = `data:image/jpeg;base64,${robot.avatar_data}`; + } else { + document.getElementById('editAvatarPreview').src = ''; + } + + // 打开模态框 + const editModal = new bootstrap.Modal(document.getElementById('editRobotModal')); + editModal.show(); +} + +// 初始化删除机器人模态框 +function initDeleteRobotModal() { + const confirmDeleteButton = document.getElementById('confirmDeleteRobot'); + + confirmDeleteButton.addEventListener('click', async function() { + const robotId = document.getElementById('deleteRobotId').value; + + // 显示删除中状态 + const originalButtonText = confirmDeleteButton.innerHTML; + confirmDeleteButton.disabled = true; + confirmDeleteButton.innerHTML = ' 删除中...'; + + try { + const response = await fetch(`/imgsearcherApi/api/robots/${robotId}`, { + method: 'DELETE' + }); + + const data = await response.json(); + + if (data.error) { + alert(`删除失败: ${data.error}`); + return; + } + + // 关闭模态框 + const deleteModal = bootstrap.Modal.getInstance(document.getElementById('deleteRobotModal')); + deleteModal.hide(); + + // 刷新机器人列表 + loadRobots(); + + // 显示成功消息 + alert('机器人角色删除成功!'); + + } catch (error) { + alert(`删除失败: ${error.message}`); + } finally { + // 恢复按钮状态 + confirmDeleteButton.disabled = false; + confirmDeleteButton.innerHTML = originalButtonText; + } + }); +} + +// 打开删除机器人模态框 +function openDeleteRobotModal(robotId, robotName) { + document.getElementById('deleteRobotId').value = robotId; + document.getElementById('deleteRobotModalLabel').textContent = `确认删除 "${robotName}"`; + document.querySelector('#deleteRobotModal .modal-body p').textContent = `确定要删除机器人角色 "${robotName}" 吗?此操作不可撤销。`; + + // 打开模态框 + const deleteModal = new bootstrap.Modal(document.getElementById('deleteRobotModal')); + deleteModal.show(); +} diff --git a/app/templates/chat.html b/app/templates/chat.html index 443f0f6..7096b06 100644 --- a/app/templates/chat.html +++ b/app/templates/chat.html @@ -25,12 +25,29 @@
上传图片
-
- - -
支持JPG、PNG、JPEG、GIF格式
+
+
+
+ + +
支持JPG、PNG、JPEG、GIF格式
+
+
+
+
+ + +
选择一个机器人角色进行对话
+
+
+
+
+ + 管理机器人角色
-
diff --git a/app/templates/index.html b/app/templates/index.html index 4f00495..03a5bad 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -29,6 +29,9 @@ +
@@ -43,15 +46,17 @@
支持JPG、PNG、BMP格式,最短边至少50px,最长边最大4096px
- +
- +
- +
diff --git a/app/templates/robots.html b/app/templates/robots.html new file mode 100644 index 0000000..99587bd --- /dev/null +++ b/app/templates/robots.html @@ -0,0 +1,186 @@ + + + + + + 机器人角色管理 - 地瓜机器人 + + + + + + +
+
+

机器人角色管理

+

创建和管理您的机器人角色

+ +
+ +
+
+
+
+
添加新机器人角色
+
+
+
+
+ + +
+
+ + +
+ 头像预览 +
+
+
+
+
+ + +
描述机器人的性格、背景和特点,这些信息将用于图片对话中
+
+
+
+ +
+
+
+
+
+ +
+
+

现有机器人角色

+
+
+ 加载中... +
+

加载机器人角色...

+
+
+ 暂无机器人角色,请添加一个新的机器人角色。 +
+
+ +
+
+ + + + + + + + + + +