File size: 4,945 Bytes
94179db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import streamlit as st
from huggingface_hub import hf_hub_download
from llama_cpp import Llama
import time

# --- 1. 页面基础配置 ---
st.set_page_config(
    page_title="Llama 3.2 AI Assistant",
    page_icon="🤖",
    layout="wide",  # 使用宽屏模式
    initial_sidebar_state="expanded"
)

# --- 2. 自定义 CSS (美化界面) ---
st.markdown("""
<style>
    /* 隐藏 Streamlit 默认的汉堡菜单和页脚 */
    #MainMenu {visibility: hidden;}
    footer {visibility: hidden;}
    header {visibility: hidden;}
    
    /* 调整主容器的顶部 padding,让内容更紧凑 */
    .block-container {
        padding-top: 2rem;
        padding-bottom: 2rem;
    }
    
    /* 美化侧边栏 */
    section[data-testid="stSidebar"] {
        background-color: #f7f9fc; /* 浅灰蓝背景 */
    }
    
    /* 自定义标题样式 */
    .title-text {
        font-family: 'Helvetica Neue', sans-serif;
        font-weight: 700;
        font-size: 2.5rem;
        color: #1E88E5; /* 科技蓝 */
        text-align: center;
        margin-bottom: 20px;
    }
    
    .subtitle-text {
        font-family: 'Helvetica Neue', sans-serif;
        font-weight: 400;
        font-size: 1.1rem;
        color: #666;
        text-align: center;
        margin-bottom: 40px;
    }
</style>
""", unsafe_allow_html=True)

# --- 3. 标题区域 ---
st.markdown('<div class="title-text">🤖 Llama 3.2-3B AI Assistant</div>', unsafe_allow_html=True)
st.markdown('<div class="subtitle-text">Powered by Marcus719/Llama-3.2-3B-changedata-Lab2-GGUF</div>', unsafe_allow_html=True)

# --- 4. 侧边栏 (控制面板) ---
with st.sidebar:
    st.image("https://huggingface.co/front/assets/huggingface_logo-noborder.svg", width=50)
    st.header("⚙️ 控制面板")
    
    # 参数设置
    temperature = st.slider("Temperature (创造性)", min_value=0.1, max_value=1.5, value=0.7, step=0.1, help="值越高,回答越随机;值越低,回答越严谨。")
    max_tokens = st.slider("Max Tokens (最大长度)", min_value=64, max_value=2048, value=512, step=64)
    
    st.divider()
    
    # 系统提示词 (System Prompt)
    system_prompt = st.text_area(
        "系统设定 (System Prompt)", 
        value="You are a helpful and polite AI assistant.",
        height=100
    )
    
    st.divider()
    
    # 清除历史按钮
    if st.button("🗑️ 清除对话历史", use_container_width=True):
        st.session_state.messages = []
        st.rerun()

    st.markdown("---")
    st.markdown("Optimization: **Unsloth Q4_K_M**")

# --- 5. 模型加载逻辑 ---
REPO_ID = "Marcus719/Llama-3.2-3B-changedata-Lab2-GGUF"
FILENAME = "unsloth.Q4_K_M.gguf"

@st.cache_resource
def load_model():
    model_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME)
    llm = Llama(
        model_path=model_path,
        n_ctx=4096,
        n_threads=2, # HF Spaces free tier limit
        verbose=False
    )
    return llm

try:
    if "llm" not in st.session_state:
        with st.spinner("🚀 正在启动 AI 引擎,请稍候..."):
            st.session_state.llm = load_model()
except Exception as e:
    st.error(f"模型加载失败: {e}")

# --- 6. 聊天逻辑 ---

# 初始化历史
if "messages" not in st.session_state:
    st.session_state.messages = []

# 显示历史消息
for message in st.session_state.messages:
    # 设置不同的头像
    avatar = "🧑‍💻" if message["role"] == "user" else "🤖"
    with st.chat_message(message["role"], avatar=avatar):
        st.markdown(message["content"])

# 处理用户输入
if prompt := st.chat_input("在此输入您的问题..."):
    # 1. 显示用户输入
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user", avatar="🧑‍💻"):
        st.markdown(prompt)

    # 2. 生成 AI 回复
    with st.chat_message("assistant", avatar="🤖"):
        message_placeholder = st.empty()
        full_response = ""
        
        # 构建带 System Prompt 的消息列表
        messages_payload = [{"role": "system", "content": system_prompt}] + [
            {"role": m["role"], "content": m["content"]}
            for m in st.session_state.messages
        ]
        
        stream = st.session_state.llm.create_chat_completion(
            messages=messages_payload,
            stream=True,
            max_tokens=max_tokens,
            temperature=temperature
        )
        
        for chunk in stream:
            if "content" in chunk["choices"][0]["delta"]:
                token = chunk["choices"][0]["delta"]["content"]
                full_response += token
                # 模拟打字机效果,稍微平滑一点显示
                message_placeholder.markdown(full_response + "▌")
        
        message_placeholder.markdown(full_response)
    
    # 3. 保存回复
    st.session_state.messages.append({"role": "assistant", "content": full_response})