简易gpt镜像搭建--Python

import os
import sys
import requests
import json
from datetime import datetime
from flask import Flask, request, jsonify, render_template_string
import webbrowser
import threading
import pystray
from PIL import Image
import base64
from io import BytesIO

# 将标准输出和标准错误重定向到一个文件
log_file = open('error.log', 'a')
sys.stdout = log_file
sys.stderr = log_file

# 从环境变量读取API密钥
#运行“ $env:OPENAI_API_KEY="your_api_key_here" ”配置
api_key = os.getenv("OPENAI_API_KEY")

if not api_key:
    raise ValueError("API key not found. Please set the OPENAI_API_KEY environment variable.")

# 打印部分API密钥进行调试
print(f"Using API Key: {api_key[:5]}...{api_key[-5:]}")

# 更新 API URL
api_url = "https://api.oneabc.org/v1/chat/completions"  #替换为你对应的api地址

# 请求头,包含 API 密钥
headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": "application/json"
}

# 存储记录的文件路径
records_file_path = "api_records.json"

# Flask app 初始化
app = Flask(__name__)

# 函数:读取记录
def read_records(file_path):
    if os.path.exists(file_path):
        with open(file_path, 'r', encoding='utf-8') as file:
            try:
                records = json.load(file)
            except json.JSONDecodeError:
                records = []
    else:
        records = []
    return records

# 函数:保存记录
def save_record(file_path, record):
    records = read_records(file_path)
    records.append(record)
    with open(file_path, 'w', encoding='utf-8') as file:
        json.dump(records, file, ensure_ascii=False, indent=4)

# 函数:获取响应
def get_response(data):
    try:
        print("Sending request to OpenAI API...")
        print("Request data:", data)
        response = requests.post(api_url, headers=headers, json=data, timeout=60)  # 增加超时时间到60秒
        response.raise_for_status()
        response_data = response.json()
        print("Response received from OpenAI API:", response_data)

        # 存储请求和响应记录
        record = {
            "timestamp": datetime.now().isoformat(),
            "request": data,
            "response": response_data
        }
        save_record(records_file_path, record)
        return response_data
    except requests.exceptions.RequestException as e:
        # 打印详细的错误信息
        print(f"HTTP request failed: {e}")
        if e.response:
            print("Response status code:", e.response.status_code)
            print("Response content:", e.response.content.decode())
    except json.JSONDecodeError:
        print("Failed to parse response as JSON.")
        print("Response content:", response.text)
    return None

@app.route("/")
def index():
    return render_template_string(r'''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Enhanced GPT-4 Chat</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css">
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            margin: 0;
            padding: 0;
            display: flex;
            flex-direction: column;
            height: 100vh;
        }
        .container {
            display: flex;
            flex-direction: column;
            flex: 1;
            overflow: hidden;
        }
        .chat-log {
            flex: 1;
            border: 1px solid #ccc;
            padding: 10px;
            overflow-y: auto;
            padding-bottom: 70px; /* 确保底部有足够的空间显示完整的内容 */
        }
        .input-area-container {
            position: fixed;
            bottom: 0;
            width: 100%;
            display: flex;
            border-top: 1px solid #ccc;
            background-color: #fff;
        }
        .input-area {
            flex: 1;
            height: 50px;
            padding: 10px;
            border: none;
            resize: none;
        }
        .send-btn {
            width: 80px;
            height: 70px;
            background-color: #007bff;
            color: white;
            border: none;
            cursor: pointer;
        }
        .send-btn:hover {
            background-color: #0056b3;
        }
        .message {
            margin: 20px 0; /* 增大对话框之间的空隙 */
            padding: 10px;
            border-radius: 10px;
            position: relative;
        }
        .message.user {
            background-color: #d1d1d1;
            text-align: right;
        }
        .message.assistant {
            background-color: #b3d1ff;
        }
        .code-container {
            position: relative;
        }
        .copy-btn-container {
            display: flex;
            justify-content: space-between;
            margin-top: 5px;
        }
        button.copy-btn {
            padding: 5px 10px;
            border: none;
            background: #007bff;
            color: white;
            cursor: pointer;
        }
        button.copy-btn:hover {
            background: #0056b3;
        }
        .copy-btn-right {
            position: absolute;
            top: 5px;
            right: 5px;
        }
        #result-dialog {
            display: none;
            position: fixed;
            top: 20%;
            left: 50%;
            transform: translate(-50%, -20%);
            background: white;
            padding: 20px;
            border: 1px solid #ccc;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            z-index: 1000;
        }
        #result-dialog textarea {
            width: 100%;
            height: 200px;
            margin-bottom: 10px;
        }
        #result-dialog .dialog-buttons {
            display: flex;
            justify-content: space-between;
        }
        .btn {
            padding: 10px 20px;
            margin: 5px;
            border: none;
            background-color: #007bff;
            color: white;
            cursor: pointer;
        }
        .btn:hover {
            background-color: #0056b3;
        }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dompurify@2.3.3/dist/purify.min.js"></script>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
    <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
    <script>
        // MathJax 配置
        window.MathJax = {
            tex: {
                inlineMath: [['$', '$'], ['\\(', '\\)']],
                displayMath: [['$$', '$$'], ['\\[', '\\]']],
                processEscapes: true,
                processEnvironments: true
            },
            options: {
                ignoreHtmlClass: "tex2jax_ignore",
                processHtmlClass: "tex2jax_process"
            }
        };

        document.addEventListener("DOMContentLoaded", () => {
            document.querySelectorAll('pre code').forEach((block) => {
                hljs.highlightBlock(block);
                const pre = block.parentElement;
                pre.classList.add('code-container');
                const copyButton = document.createElement('button');
                copyButton.className = 'copy-btn copy-btn-right';
                copyButton.textContent = 'Copy';
                copyButton.onclick = () => copyToClipboard(block);
                pre.appendChild(copyButton);
            });
        });

        let messages = [
            {"role": "system", "content": "You are a helpful assistant. When writing mathematical expressions, use the $$...$$ format in Markdown."}
        ];

        function sendMessage() {
            const userInput = document.getElementById("input-text").value;
            if (userInput.toLowerCase() === "exit") {
                alert("Exiting...");
                return;
            }

            const chatLog = document.getElementById("chat-log");
            chatLog.innerHTML += `
                <div class="message user">
                    ${userInput}
                    <div class="copy-btn-container" style="justify-content: flex-end;">
                        <button class="copy-btn" onclick="copyToClipboardText(this)">Copy</button>
                    </div>
                </div>`;

            fetch("/send_message", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify({
                    message: userInput,
                    messages: messages
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.assistant_message) {
                    let assistantMessage = DOMPurify.sanitize(marked.parse(data.assistant_message));

                    // 调试输出
                    console.log("Assistant raw message: ", data.assistant_message);

                    // 替换自定义数学标记为MathJax标记
                    assistantMessage = assistantMessage
                        .replace(/\\\(/g, '\\(')    // 替换 \( 为 \(
                        .replace(/\\\)/g, '\\)')    // 替换 \) 为 \)
                        .replace(/\$\$/g, '$$');    // 确保 $$ 保持不变

                    // 调试输出
                    console.log("Assistant sanitized message: ", assistantMessage);

                    chatLog.innerHTML += `
                        <div class="message assistant">
                            ${assistantMessage}
                            <div class="copy-btn-container" style="justify-content: flex-start;">
                                <button class="copy-btn" onclick="copyToClipboardText(this)">Copy</button>
                            </div>
                        </div>`;
                    messages = data.messages;
                    document.getElementById("input-text").value = "";

                    // 重新渲染MathJax
                    MathJax.typesetPromise().then(() => {
                        chatLog.scrollTop = chatLog.scrollHeight; // 滚动到底部
                        document.querySelectorAll('pre code').forEach((block) => {
                            hljs.highlightBlock(block);
                            const pre = block.parentElement;
                            pre.classList.add('code-container');
                            if (!pre.querySelector('.copy-btn-right')) {
                                const copyButton = document.createElement('button');
                                copyButton.className = 'copy-btn copy-btn-right';
                                copyButton.textContent = 'Copy';
                                copyButton.onclick = () => copyToClipboard(block);
                                pre.appendChild(copyButton);
                            }
                        });
                    });
                } else {
                    chatLog.innerHTML += `<div class="message error">Failed to get response</div>`;
                }
            })
            .catch(error => {
                chatLog.innerHTML += `<div class="message error">Error: ${error}</div>`;
            });
        }

        function copyToClipboardText(button) {
            const text = button.closest('.message').textContent.replace("Copy", "").trim();
            navigator.clipboard.writeText(text);
            alert("Copied to clipboard: " + text);
        }

        function copyToClipboard(block) {
            const text = block.textContent;
            navigator.clipboard.writeText(text);
            alert("Copied to clipboard: " + text);
        }

        function showResultDialog() {
            const resultContent = messages.map(msg => `${msg.role}: ${msg.content}`).join('\n\n');
            document.getElementById("result-content").value = resultContent;
            document.getElementById("result-dialog").style.display = 'block';
        }

        function copyResult() {
            const resultContent = document.getElementById("result-content");
            resultContent.select();
            document.execCommand("copy");
            alert("Results copied to clipboard!");
        }

        function closeResultDialog() {
            document.getElementById("result-dialog").style.display = 'none';
        }
    </script>
</head>
<body>
    <div class="container">
        <div class="chat-log" id="chat-log"></div>
        <div class="input-area-container">
            <textarea class="input-area" id="input-text"></textarea>
            <button class="send-btn" onclick="sendMessage()">Send</button>
            <button class="send-btn" onclick="showResultDialog()">Show Results</button>
        </div>
    </div>
    <div id="result-dialog">
        <textarea id="result-content" readonly></textarea>
        <div class="dialog-buttons">
            <button class="btn" onclick="copyResult()">Copy</button>
            <button class="btn" onclick="closeResultDialog()">Close</button>
        </div>
    </div>
</body>
</html>
    ''')

@app.route("/send_message", methods=["POST"])
def send_message():
    user_input = request.json.get("message")
    messages = request.json.get("messages", [])

    messages.append({"role": "user", "content": user_input})

    data = {
        "model": "gpt-4o-mini-2024-07-18",   #替换为你所需的模型
        "messages": messages
    }

    response_data = get_response(data)

    if response_data:
        assistant_message = response_data['choices'][0]['message']['content']
        messages.append({"role": "assistant", "content": assistant_message})

        return jsonify({
            "assistant_message": assistant_message,
            "messages": messages
        })
    else:
        print("Failed to get a valid response.")
        return jsonify({"error": "Failed to get response"}), 500

# 系统托盘图标创建
def create_image():
    icon_base64_data = (
        "xxx"
    )
    image_data = base64.b64decode(icon_base64_data)
    image = Image.open(BytesIO(image_data))
    return image

# 退出Flask应用和系统托盘图标
def quit_application(icon, item):
    icon.stop()
    os._exit(0)

# 系统托盘图标设置
def setup_tray():
    icon = pystray.Icon("test_icon")
    icon.icon = create_image()
    icon.title = "Test Icon"
    icon.menu = pystray.Menu(
        pystray.MenuItem("Open", lambda: webbrowser.open("http://localhost:5000")),
        pystray.MenuItem("Quit", quit_application)
    )
    icon.run()

if __name__ == "__main__":
    # 启动 Flask 应用
    flask_thread = threading.Thread(target=lambda: app.run(debug=True, use_reloader=False))
    flask_thread.start()
    # 启动系统托盘图标
    setup_tray()



'''
创建一个新Pyhon脚本

# convert_icon.py
import base64

# 使用绝对路径读取图标文件
icon_path = "替换为你的路径"
with open(icon_path, "rb") as png_file:
    icon_bytes = png_file.read()

# 将字节数组转换为Base64编码字符串
encoded_icon = base64.b64encode(icon_bytes).decode('utf-8')

# 指定输出文件路径
output_path = "替换为你的路径"

# 将Base64编码字符串写入指定文件
with open(output_path, "w") as text_file:
    text_file.write(encoded_icon)

print(f"Base64 encoded icon has been saved to {output_path}")
'''

'''
# 系统托盘图标创建
def create_image():
    icon_base64_data = (
        "xxx"
    )
把xxx替换为输出的结果
'''

web页面,具体的看注释,可以markdown

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值