File size: 9,908 Bytes
10911bb
4d41c6a
 
 
10911bb
4d41c6a
12013c3
4d41c6a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41768b1
4d41c6a
 
41768b1
4d41c6a
41768b1
 
4d41c6a
41768b1
4d41c6a
41768b1
4d41c6a
 
 
41768b1
4d41c6a
41768b1
 
 
4d41c6a
41768b1
4d41c6a
41768b1
 
 
4d41c6a
41768b1
 
 
 
4d41c6a
41768b1
4d41c6a
41768b1
 
 
4d41c6a
41768b1
4d41c6a
 
41768b1
 
4d41c6a
41768b1
 
 
4d41c6a
41768b1
4d41c6a
41768b1
 
 
 
 
 
 
4d41c6a
efc5557
 
41768b1
 
 
 
efc5557
41768b1
4d41c6a
 
 
41768b1
4d41c6a
41768b1
 
 
 
 
4d41c6a
41768b1
4d41c6a
 
41768b1
 
4d41c6a
41768b1
4d41c6a
41768b1
4d41c6a
41768b1
 
 
 
 
 
 
 
 
 
4d41c6a
41768b1
 
 
 
 
4d41c6a
41768b1
4d41c6a
41768b1
 
 
4d41c6a
41768b1
 
4d41c6a
41768b1
 
 
 
 
 
 
 
 
 
 
4d41c6a
10911bb
 
4d41c6a
 
 
 
 
 
 
 
 
 
10911bb
4d41c6a
10911bb
4d41c6a
 
 
 
 
 
 
 
 
 
 
10911bb
4d41c6a
10911bb
4d41c6a
 
 
 
 
 
 
 
 
10911bb
4d41c6a
 
 
 
 
 
 
 
 
 
10911bb
 
4d41c6a
 
 
 
10911bb
4d41c6a
 
 
 
 
41768b1
4d41c6a
 
 
 
 
12013c3
4d41c6a
 
 
 
 
 
 
 
12013c3
4d41c6a
 
 
10911bb
4d41c6a
 
 
 
10911bb
4d41c6a
10911bb
4d41c6a
61881b1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
import gradio as gr
import openai
import os
import time

# --- 配置 ---
ARK_API_KEY = os.environ.get("ARK_API_KEY")
if not ARK_API_KEY:
    raise ValueError("ARK_API_KEY not found. Please set it in Space Secrets.")

client = openai.OpenAI(api_key=ARK_API_KEY, base_url="https://ark.cn-beijing.volces.com/api/v3")

# --- 可选模型列表 ---
# 键是用户看到的显示名称,值是实际的API模型ID
AVAILABLE_MODELS = {
    "DeepSeek R1 (250120)": "deepseek-r1-250120",
    "DeepSeek v3 (250324)": "deepseek-v3-250324" # 示例,你需要有这个模型的权限
    # 你可以添加更多你账户下可用的模型
    # "模型名称A": "模型ID_A",
    # "模型名称B": "模型ID_B",
}
DEFAULT_MODEL_DISPLAY_NAME = list(AVAILABLE_MODELS.keys())[0] # 默认选择第一个模型

# --- 自定义 CSS ---
custom_css = """
body {
    background-color: #e9ecef; /* 稍微调暗一点背景,让前景更突出 */
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.gradio-container {
    border-radius: 20px !important; /* 稍微增大圆角 */
    box-shadow: 0 8px 24px rgba(0,0,0,0.15) !important; /* 增强阴影 */
    background-color: #ffffff;
    /* border: 1px solid #dee2e6 !important; */ /* (可选)添加一个细边框 */
}

.message-bubble-user {
    background-color: #007bff !important;
    color: white !important;
    border-radius: 20px 20px 5px 20px !important; /* 调整圆角使其更有特色 */
    align-self: flex-end;
    max-width: 75%; /* 稍微增加最大宽度 */
    box-shadow: 3px 3px 8px rgba(0,0,0,0.2) !important; /* 添加阴影使其浮动 */
    margin-bottom: 10px !important; /* 增加一点底部间距 */
}

.message-bubble-bot {
    background-color: #f8f9fa !important; /* 使用更亮的背景增加对比 */
    color: #212529 !important; /* 使用更深的文字颜色增加对比 */
    border-radius: 20px 20px 20px 5px !important; /* 调整圆角 */
    align-self: flex-start;
    max-width: 75%; /* 稍微增加最大宽度 */
    border: 1px solid #ced4da !important; /* 添加边框使其更清晰 */
    box-shadow: -3px 3px 8px rgba(0,0,0,0.1) !important; /* 添加阴影 */
    margin-bottom: 10px !important; /* 增加一点底部间距 */
}

.message-bubble-user .message-text, .message-bubble-bot .message-text {
    font-size: 1.05rem !important; /* 稍微增大字体 */
    padding: 12px 18px !important; /* 增加内边距 */
    line-height: 1.6 !important; /* 增加行高,提高可读性 */
}

.avatar-container img {
    border-radius: 50% !important;
    width: 48px !important; /* 增大头像尺寸 */
    height: 48px !important; /* 增大头像尺寸 */
    object-fit: cover;
    margin: 8px !important; /* 增加外边距 */
    box-shadow: 0 2px 6px rgba(0,0,0,0.25) !important; /* 增强阴影 */
    border: 2px solid #ffffff !important; /* 给头像加一个白色边框,使其从背景中突出 */
}

.textbox textarea {
    border-radius: 25px !important; /* 增大圆角 */
    border: 2px solid #343a40 !important; /* 加粗边框 */
    background-color: #495057 !important;
    color: #f8f9fa !important;
    padding: 12px 18px !important; /* 增加内边距 */
    font-size: 1.05rem !important; /* 稍微增大字体 */
    box-shadow: inset 0 1px 3px rgba(0,0,0,0.2) !important; /* 添加内阴影增加深度感 */
}

.textbox textarea:focus {
    border-color: #007bff !important;
    background-color: #5a6268 !important;
    box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.35) !important; /* 增强聚焦阴影 */
    /* transform: scale(1.01); */ /* (可选)聚焦时轻微放大 */
}

.submit-button {
    background-color: #007bff !important;
    color: white !important;
    border-radius: 25px !important; /* 增大圆角 */
    font-weight: bold !important;
    font-size: 1.1rem !important; /* 增大字体 */
    padding: 12px 25px !important; /* 增加内边距 */
    transition: all 0.2s ease-in-out; /* 平滑所有过渡 */
    box-shadow: 0 3px 6px rgba(0, 123, 255, 0.3) !important; /* 添加阴影 */
    border: none !important; /* 移除默认边框(如果存在) */
}

.submit-button:hover {
    background-color: #0056b3 !important;
    box-shadow: 0 5px 10px rgba(0, 86, 179, 0.4) !important; /* 悬停时增强阴影 */
    transform: translateY(-2px); /* 悬停时轻微上移 */
}

.gr-prose h1, .gr-prose h2, .gr-prose h3 {
    color: #212529; /* 更深的颜色 */
    text-align: center;
    margin-bottom: 0.75em !important; /* 增加标题下的间距 */
    text-shadow: 1px 1px 2px rgba(0,0,0,0.1); /* 给标题添加轻微文字阴影 */
}
.gr-prose h1 {
    font-size: 2.8em !important; /* 增大 H1 */
    font-weight: 700 !important;
}
.gr-prose h2 {
    font-size: 2.2em !important; /* 增大 H2 */
    font-weight: 600 !important;
}
.gr-prose h3 {
    font-size: 1.8em !important; /* 增大 H3 */
    font-weight: 600 !important;
}

.gr-prose p {
    color: #343a40; /* 更深的颜色 */
    text-align: center;
    font-size: 1.15em !important; /* 增大段落字体 */
    line-height: 1.7 !important; /* 增加行高 */
    margin-bottom: 1.5em !important; /* 增加段落下间距 */
}

/* 为下拉菜单添加一些边距和视觉强调 */
.gradio-dropdown {
    margin-top: 10px !important; /* 增加上方外边距 */
    margin-bottom: 25px !important; /* 显著增加下方外边距,使其与其他元素分隔开 */
    /* 如果想让下拉框本身更突出,可能需要针对其内部元素如 .gr-select select 或类似的选择器 */
    /* 例如(需要根据实际Gradio版本检查选择器): */
    /*
    select {
        border-width: 2px !important;
        border-color: #007bff !important;
        font-weight: bold !important;
    }
    */
}
"""

# --- 聊天逻辑 (修改以接收模型ID) ---
def call_openai_with_history(message: str, history: list, selected_model_display_name: str):
    """
    调用 OpenAI API,并处理聊天历史。
    message: 当前用户输入
    history: Gradio 的聊天历史
    selected_model_display_name: 用户从下拉菜单选择的模型显示名称
    """
    actual_model_id = AVAILABLE_MODELS.get(selected_model_display_name, AVAILABLE_MODELS[DEFAULT_MODEL_DISPLAY_NAME])
    print(f"使用模型: {actual_model_id}") # 调试信息

    messages_for_api = [{"role": "system", "content": "你是一个乐于助人、风趣幽默的AI助手。"}]

    for user_msg, bot_msg in history:
        # Gradio 的 history 格式是 [[user_msg1, bot_msg1], ...]
        # 当 chatbot type='messages' 时,history 格式是 [{'role':'user', 'content':...}, {'role':'assistant', 'content':...}]
        # 这里需要根据 chatbot 的 type 来调整 history 的处理方式
        # 由于 chatbot_component.type = 'messages', history 已经是 OpenAI 期望的格式了
        # 但是 ChatInterface 传递给 fn 的 history 仍然是 [[user, bot], ...] 格式
        # 所以我们仍然需要转换
        if isinstance(user_msg, str): # 确保是 [[user, bot], ...] 格式
            messages_for_api.append({"role": "user", "content": user_msg})
        if bot_msg and isinstance(bot_msg, str):
             messages_for_api.append({"role": "assistant", "content": bot_msg})

    messages_for_api.append({"role": "user", "content": message})

    try:
        response = client.chat.completions.create(
            model=actual_model_id, # 使用选择的模型ID
            messages=messages_for_api,
            temperature=0.7,
            max_tokens=1024, # 可以适当增加 max_tokens
        )
        bot_response = response.choices[0].message.content
        return bot_response

    except openai.APIError as e:
        error_message = f"OpenAI API Error: {e}"
        print(error_message)
        if hasattr(e, 'message'):
            return f"抱歉,调用AI服务时出错: {e.message}"
        return f"抱歉,调用AI服务时出错: {str(e)}"
    except Exception as e:
        error_message = f"An unexpected error occurred: {e}"
        print(error_message)
        return "抱歉,发生了一个未知错误。"


# --- Gradio 界面 ---
with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
    gr.Markdown("# 🤖 Starry的智能AI聊天伙伴")
    gr.Markdown("与基于大模型的AI助手进行友好对话吧!")

    # 添加模型选择下拉菜单
    model_selector = gr.Dropdown(
        choices=list(AVAILABLE_MODELS.keys()), # 用户看到的选项是显示名称
        value=DEFAULT_MODEL_DISPLAY_NAME,     # 默认值
        label="选择AI模型",
        info="更改模型后,请刷新窗口,新的对话将使用所选模型。",
        elem_classes="gradio-dropdown" # 应用自定义CSS类 (可选)
    )

    chatbot_component = gr.Chatbot(
        label="聊天窗口",
        type='messages', # 确保使用 'messages' 类型,以便更好地处理头像和角色
        avatar_images=("images/user_avatar.png", "images/bot_avatar.png"),
        height=500,
        show_label=False # 隐藏 Chatbot 自己的标签,因为上面已经有标题了
    )

    chat_interface = gr.ChatInterface(
        fn=call_openai_with_history,
        chatbot=chatbot_component,
        type='messages',
        additional_inputs=[model_selector], # 将下拉菜单作为额外输入传递给 fn
        submit_btn="➤ 发送",
    )

    # 如果你的 Gradio 版本较低,可能没有 avatar_images 直接在 Chatbot 构造函数里
    # 如果头像图片不显示,请确保 `images` 文件夹和里面的 `user_avatar.png` `bot_avatar.png`
    # 文件与你的 app.py 在同一级别,或者你提供了正确的相对/绝对路径。
    # 对于 Hugging Face Spaces,通常将 `images` 文件夹放在仓库根目录。

# --- 启动应用 ---
if __name__ == "__main__":
    # demo.queue() # 如果 API 调用时间较长,可以启用队列
    demo.launch()