File size: 6,279 Bytes
428c36f
dc77ac5
9188cd5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dc77ac5
d25a471
428c36f
9188cd5
 
428c36f
 
dc77ac5
9188cd5
428c36f
 
 
9188cd5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dc77ac5
9188cd5
 
 
 
 
428c36f
9188cd5
 
 
428c36f
 
 
d25a471
9188cd5
 
 
 
 
 
 
d25a471
9188cd5
 
 
 
 
 
 
 
 
dc77ac5
9188cd5
 
 
 
 
 
 
 
 
 
428c36f
d25a471
9188cd5
 
 
428c36f
 
9188cd5
 
428c36f
 
 
 
 
 
 
 
 
 
 
 
 
9188cd5
 
 
428c36f
 
 
d25a471
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
import gradio as gr
import os
# 移除 Hugging Face Inference Client 相關導入
# from huggingface_hub import InferenceClient
import google.generativeai as genai # <-- 導入 Google Generative AI 庫

# 移除 Hugging Face Token 讀取和 Client 初始化
# hf_token = os.getenv("HF_TOKEN")
# if not hf_token:
#     print("Error: Hugging Face Token (HF_TOKEN) not found...")
# client = InferenceClient("...", token=hf_token)

# 讀取 Google AI API 金鑰 Secret
# 確保您的 Secret 名稱為 GOOGLE_API_KEY
google_api_key = os.getenv("GOOGLE_API_KEY")

# 檢查金鑰是否存在
if not google_api_key:
    # 在 Gradio 應用啟動時顯示錯誤或拋出異常
    error_message = "錯誤:Google AI API Key (GOOGLE_API_KEY) Secret 未設定。請在 Hugging Face Space 設定中添加。"
    print(error_message)
    # 為了讓 Space 啟動不失敗,可以在這裡暫時顯示錯誤,但後續 API 呼叫會失敗
    # 也可以選擇直接 sys.exit(1) 讓 Space 構建或啟動失敗,提示使用者設定 Secret
    # import sys
    # sys.exit(1)

# 配置 genai 庫
genai.configure(api_key=google_api_key)

# 選擇一個支援聊天的 Gemini 模型
# 'gemini-pro' 是常用的文字聊天模型
# 模型名稱可以在 Google AI 文檔中找到
MODEL_NAME = "gemini-pro"

# System Prompt 讀取方式保持不變 (從環境變數讀取,請確保 Secret 名稱一致)
# 假設你在 Hugging Face Space Secrets 中設定了一個名為 SYSTEM_PROMPT 的 Secret。
SYSTEM_PROMPT_CONTENT = os.getenv("SYSTEM_PROMPT", "你現在是一名專業的昆蟲學系的大學教授,所以你要用專業的角度簡單跟學生回答昆蟲的知識,請盡可能使用流暢的中文回答。")


# 修改 respond 函數,使用 Gemini API 進行回覆生成
# 函數簽名與之前修改後的版本一致
def respond(
    message,
    history: list[tuple[str, str]], # history 包含之前的對話 [(user, assistant), ...]
    max_tokens, # 對應 generation_config 的 max_output_tokens
    temperature,
    top_p,
):
    # 如果金鑰不存在,不進行 API 呼叫,直接返回錯誤訊息
    if not google_api_key:
         yield error_message
         return # 結束函數執行

    # 構建發送給 Gemini API 的 messages 列表
    # Gemini API 的 generate_content 方法,當包含 history 時,通常期望格式為
    # [{'role': 'user', 'parts': ['...']}, {'role': 'model', 'parts': ['...']}, ...]
    # System Instruction 在 generate_content 中通常通過將其內容放在第一條 'user' 消息之前實現
    # 或者將其作為單獨的 system_instruction 參數(如果可用且通過 generate_content 傳遞)
    # 這裡我們採用將 system prompt 內容放在第一條實際 user 消息前的策略(如果歷史為空)
    # 或直接將 system prompt 作為一個獨立的 'user'/'model' 對話對放在歷史最前面 (方便處理 Gradio history)
    # 雖然不太符合官方 system_instruction 參數的用法,但能適應 Gradio 的 history 結構

    messages_for_gemini = []

    # 將 System Prompt 內容加入消息列表的開頭
    # 這是為了讓模型理解指令,儘管不是 Gemini 官方推薦的 system_instruction 參數
    # 我們將其作為一個獨立的 'user' 訊息放在最前面,後面跟一個 'model' 訊息作為確認
    messages_for_gemini.append({"role": "user", "parts": [SYSTEM_PROMPT_CONTENT]})
    messages_for_gemini.append({"role": "model", "parts": ["好的,我明白了,請開始。"]}) # 假裝模型確認了指令


    # 將 Gradio 提供的 history 轉換為 Gemini API 的格式
    # Gradio history 是 [(user_msg, bot_msg), ...]
    for user_msg, bot_msg in history:
        # 確保訊息不為 None 或空字串,避免 API 錯誤
        if user_msg:
            messages_for_gemini.append({"role": "user", "parts": [user_msg]})
        if bot_msg:
            messages_for_gemini.append({"role": "model", "parts": [bot_msg]})

    # 加入當前使用者訊息
    if message:
        messages_for_gemini.append({"role": "user", "parts": [message]})


    response = ""
    try:
        # 呼叫 Gemini API
        # 使用 generate_content 方法
        model = genai.GenerativeModel(MODEL_NAME)

        # 設定生成參數
        generation_config = genai.types.GenerationConfig(
            max_output_tokens=max_tokens, # 對應 max_tokens
            temperature=temperature,
            top_p=top_p
            # stop_sequences=[] # 如果需要,可以在這裡設定停止標記
        )

        # 呼叫 generate_content,傳入格式化好的消息列表和生成參數
        # 對於 stream=True,它返回一個 Response 迭代器
        stream = model.generate_content(
            messages_for_gemini, # <-- 傳入構建好的消息列表
            generation_config=generation_config,
            stream=True,
        )

        # 處理流式回覆
        # 迭代 Response 迭代器,獲取 chunks
        for chunk in stream:
            # chunk.text 包含當前的文本片段
            # 需要確保 chunk.text 不是 None 或空字串
            if hasattr(chunk, 'text') and chunk.text:
                 response += chunk.text
                 yield response # 逐步返回結果

    except Exception as e:
        print(f"Error during Gemini inference: {e}")
        # 顯示更詳細的錯誤信息給使用者
        yield f"生成回覆時發生錯誤: {e}"


# Gradio ChatInterface 設定保持不變
# additional_inputs 移除 System message 文字框
demo = gr.ChatInterface(
    respond,
    additional_inputs=[
        gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
        gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
        gr.Slider(
            minimum=0.1,
            maximum=1.0,
            value=0.95,
            step=0.05,
            label="Top-p (nucleus sampling)",
        ),
    ],
    # Chatbot 组件可以考虑设置 type='messages' 并在 respond 中返回对应的格式,以消除 UserWarning
    # 例如 bot=gr.Chatbot(type='messages')
    # 这需要调整 respond 函数返回值的格式,超出当前主要任务范围,可以之后优化
)

if __name__ == "__main__":
    demo.launch()