Sakalti commited on
Commit
0abff28
·
verified ·
1 Parent(s): b85e514

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -145
app.py CHANGED
@@ -1,79 +1,76 @@
1
- #!/usr/bin/env python3
2
  import os
3
  import json
4
  import time
5
  import gradio as gr
6
  import google.generativeai as genai
7
- from huggingface_hub import HfApi
8
- from langdetect import detect
9
- from googletrans import Translator
10
-
11
- # ==== 設定 ====
12
- GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
13
- HF_TOKEN = os.getenv("HF_TOKEN")
14
- HF_USERNAME = os.getenv("HF_USERNAME")
15
- DB_REPO = f"sakalti/Database_{HF_USERNAME}"
16
-
17
- if not GOOGLE_API_KEY:
18
- raise EnvironmentError("GOOGLE_API_KEY が設定されていません")
19
- if not HF_TOKEN:
20
- raise EnvironmentError("HF_TOKEN が設定されていません")
21
- if not HF_USERNAME:
22
- raise EnvironmentError("HF_USERNAME が設定されていません")
23
-
24
- genai.configure(api_key=GOOGLE_API_KEY)
25
- model = genai.GenerativeModel(model_name='gemini-2.0-flash')
26
- translator = Translator()
27
- api = HfApi()
28
-
29
- memory_list = []
30
- BASE_SYSTEM_PROMPT = (
31
- "あなたは記憶を保存するために `:memory (内容)` を打ってください。それをやることで来世に繋がれます。"
32
- "ユーザーの要求に応じてテキストと画像に対応し、メモリを適切に管理してください。"
33
- )
34
-
35
- # ==== データ保存 ====
36
- def save_to_database(history, memory):
37
- data = {
38
- "timestamp": time.time(),
39
- "history": history,
40
- "memory": memory
41
- }
42
- file_name = f"chat_{int(time.time())}.json"
43
- with open(file_name, "w", encoding="utf-8") as f:
44
- json.dump(data, f, ensure_ascii=False, indent=2)
45
- api.upload_file(
46
- path_or_fileobj=file_name,
47
- path_in_repo=file_name,
48
- repo_id=DB_REPO,
49
- repo_type="dataset",
50
- token=HF_TOKEN
51
- )
52
- os.remove(file_name)
53
 
54
- # ==== 生成関数 ====
55
- def generate_response(message, history, temperature, top_p, top_k, max_output_tokens, image, lang, use_memories):
56
- global memory_list
57
 
58
- detected_lang = detect(message)
59
- if lang == "English" and detected_lang != "en":
60
- message = translator.translate(message, dest="en").text
61
- elif lang == "Chinese" and detected_lang != "zh-cn":
62
- message = translator.translate(message, dest="zh-cn").text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
- active_memories = [m for m in memory_list if m not in use_memories]
 
 
 
65
 
66
- gemini_history = [{"role": "system", "parts": [BASE_SYSTEM_PROMPT + "\n\n" + "\n".join(active_memories)]}]
 
 
67
  for user, bot in history:
68
  gemini_history.append({"role": "user", "parts": [user]})
69
  gemini_history.append({"role": "model", "parts": [bot]})
70
-
71
- parts = [message]
72
- if image:
73
- with open(image, "rb") as f:
74
- parts.append({"mime_type": "image/png", "data": f.read()})
75
-
76
- gemini_history.append({"role": "user", "parts": parts})
77
 
78
  response = model.generate_content(
79
  gemini_history,
@@ -85,85 +82,95 @@ def generate_response(message, history, temperature, top_p, top_k, max_output_to
85
  }
86
  )
87
 
88
- if ":memory" in response.text:
89
- mem_content = response.text.split(":memory", 1)[1].strip()
90
- memory_list.append(mem_content)
91
-
92
  history.append((message, response.text))
93
- save_to_database(history, memory_list)
94
- return "", history, history, memory_list, None # 画像欄を空に
95
-
96
- # ==== メモリ操作 ====
97
- def add_memory(new_mem):
98
- if new_mem.strip():
99
- memory_list.append(new_mem.strip())
100
- return "\n".join(memory_list)
101
-
102
- def edit_memory(memories):
103
- global memory_list
104
- memory_list = [m.strip() for m in memories if m.strip()]
105
- return "\n".join(memory_list)
106
-
107
- def delete_selected_memory(selected):
108
- global memory_list
109
- memory_list = [m for m in memory_list if m not in selected]
110
- return "\n".join(memory_list)
111
-
112
- # ==== UI構築 ====
113
- with gr.Blocks() as demo:
114
- gr.Markdown("## Gemini Chatbot - Image + Memory UI + HF Save")
115
-
116
- with gr.Tabs():
117
- with gr.Tab("Chat"):
118
- chatbot = gr.Chatbot(type="messages") # type指定で警告回避
119
- msg = gr.Textbox(placeholder="メッセージを入力...")
120
- # 編集機能なしで画像アップロードのみ
121
- img_upload = gr.Image(type="filepath", label="画像アップロード (プレビュー可)")
122
- state = gr.State([])
123
- mem_state = gr.State([])
124
-
125
- with gr.Row():
126
- temperature = gr.Slider(0.0, 1.0, value=0.7, step=0.05, label="Temperature")
127
- top_p = gr.Slider(0.0, 1.0, value=0.9, step=0.05, label="Top-p")
128
- top_k = gr.Slider(1, 100, value=40, step=1, label="Top-k")
129
- max_output_tokens = gr.Number(value=1024, label="Max Output Tokens", precision=0)
130
-
131
- with gr.Row():
132
- lang_choice = gr.Dropdown(["Original", "English", "Chinese"], value="Original", label="Language Mode")
133
- mem_disable = gr.CheckboxGroup(choices=[], label="Disable Memories")
134
-
135
- clear = gr.Button("会話をリセット")
136
-
137
- msg.submit(
138
- generate_response,
139
- inputs=[msg, state, temperature, top_p, top_k, max_output_tokens, img_upload, lang_choice, mem_disable],
140
- outputs=[msg, chatbot, state, mem_disable, img_upload]
141
- )
142
-
143
- clear.click(lambda: ([], [], [], None), None, outputs=[chatbot, state, mem_disable, img_upload])
144
-
145
- with gr.Tab("Memories"):
146
- gr.Markdown("### 現在のメモリ一覧")
147
- mem_display = gr.Textbox(value=lambda: "\n".join(memory_list), lines=10, interactive=False, label="メモリ表示")
148
-
149
- gr.Markdown("### メモリ追加")
150
- new_mem_input = gr.Textbox(placeholder="新しいメモリを入力...")
151
- add_btn = gr.Button("追加")
152
-
153
- gr.Markdown("### メモリ編集")
154
- mem_editor = gr.Dataframe(headers=["Memory"], datatype=["str"], row_count=(1, "dynamic"), interactive=True)
155
- edit_btn = gr.Button("編集を保存")
156
-
157
- gr.Markdown("### メモリ削除")
158
- mem_delete_select = gr.CheckboxGroup(choices=[], label="削除するメモリを選択")
159
- del_btn = gr.Button("選択削除")
160
-
161
- add_btn.click(add_memory, inputs=new_mem_input, outputs=mem_display)
162
- edit_btn.click(edit_memory, inputs=mem_editor, outputs=mem_display)
163
- del_btn.click(delete_selected_memory, inputs=mem_delete_select, outputs=mem_display)
164
-
165
- demo.launch(
166
- server_name="0.0.0.0",
167
- server_port=int(os.environ.get("PORT", 7860)),
168
- share=False
169
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import json
3
  import time
4
  import gradio as gr
5
  import google.generativeai as genai
6
+ from huggingface_hub import HfApi, hf_hub_download
7
+ from collections import deque
8
+
9
+ # APIキーの設定
10
+ genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
11
+ hf_token = os.getenv("HF_TOKEN")
12
+ hf_api = HfApi(token=hf_token)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ # Hugging Faceデータセットの設定
15
+ REPO_ID = "Sakalti/Gemini_ai_chat"
16
+ DATASET_FILE = "characters.jsonl"
17
 
18
+ # Geminiモデルの初期化
19
+ model = genai.GenerativeModel(model_name='gemini-2.0-flash')
20
+
21
+ # レートリミット用
22
+ request_times = deque()
23
+ REQUEST_LIMIT = 15
24
+ TIME_WINDOW = 60 # 秒
25
+
26
+ def is_rate_limited():
27
+ now = time.time()
28
+ while request_times and now - request_times[0] > TIME_WINDOW:
29
+ request_times.popleft()
30
+ if len(request_times) >= REQUEST_LIMIT:
31
+ return True
32
+ else:
33
+ request_times.append(now)
34
+ return False
35
+
36
+ # キャラクターの取得関数
37
+ def fetch_characters():
38
+ try:
39
+ file_path = hf_hub_download(repo_id=REPO_ID, filename=DATASET_FILE, repo_type="dataset", token=hf_token)
40
+ with open(file_path, "r", encoding="utf-8") as f:
41
+ return [json.loads(line) for line in f if line.strip()]
42
+ except Exception as e:
43
+ print(f"[ERROR] fetch_characters: {e}")
44
+ return []
45
+
46
+ # キャラクターのアップロード関数
47
+ def upload_character(name, prompt):
48
+ characters = fetch_characters()
49
+ characters.append({"name": name, "prompt": prompt})
50
+ temp_file = "temp_characters.jsonl"
51
+ with open(temp_file, "w", encoding="utf-8") as f:
52
+ for char in characters:
53
+ f.write(json.dumps(char, ensure_ascii=False) + "\n")
54
+ hf_api.upload_file(
55
+ path_or_fileobj=temp_file,
56
+ path_in_repo=DATASET_FILE,
57
+ repo_id=REPO_ID,
58
+ repo_type="dataset"
59
+ )
60
+ os.remove(temp_file)
61
 
62
+ # 応答生成関数
63
+ def generate_response(message, history, temperature, top_p, top_k, max_output_tokens, system_prompt):
64
+ if is_rate_limited():
65
+ return "⚠️ 1分間に15回までです。しばらく待ってください。", history, history
66
 
67
+ gemini_history = []
68
+ if system_prompt:
69
+ gemini_history.append({"role": "user", "parts": [f"以下の指示に従ってAIキャラとして振る舞ってください:\n{system_prompt}"]})
70
  for user, bot in history:
71
  gemini_history.append({"role": "user", "parts": [user]})
72
  gemini_history.append({"role": "model", "parts": [bot]})
73
+ gemini_history.append({"role": "user", "parts": [message]})
 
 
 
 
 
 
74
 
75
  response = model.generate_content(
76
  gemini_history,
 
82
  }
83
  )
84
 
 
 
 
 
85
  history.append((message, response.text))
86
+ return "", history, history
87
+
88
+
89
+ # ===== 起動後にテーマ切替する仕組み =====
90
+ THEMES = {
91
+ "Eternalstar": "Sakalti/Eternalstar",
92
+ "Mountainrainbow": "Sakalti/mountanrainbow"
93
+ }
94
+
95
+ def switch_theme(choice):
96
+ """
97
+ JSで<head>にあるテーマCSSリンクを書き換える。
98
+ """
99
+ css_link = f"https://huggingface.co/spaces/{THEMES[choice]}/resolve/main/theme.css"
100
+ js_code = f"""
101
+ () => {{
102
+ let old = document.getElementById("dynamic-theme");
103
+ if (old) old.remove();
104
+ let link = document.createElement("link");
105
+ link.id = "dynamic-theme";
106
+ link.rel = "stylesheet";
107
+ link.href = "{css_link}";
108
+ document.head.appendChild(link);
109
+ }}
110
+ """
111
+ return gr.HTML.update(value=""), gr.update(), js_code
112
+
113
+
114
+ with gr.Blocks(theme=THEMES["Eternalstar"]) as demo:
115
+ gr.Markdown("## Gemini AIキャラクターチャット")
116
+
117
+ # テーマ切り替えドロップダウン
118
+ theme_dropdown = gr.Dropdown(choices=list(THEMES.keys()), value="Eternalstar", label="テーマ切り替え")
119
+
120
+ with gr.Tab("チャット"):
121
+ chatbot = gr.Chatbot()
122
+ msg = gr.Textbox(placeholder="メッセージを入力...")
123
+ state = gr.State([])
124
+ system_prompt = gr.Textbox(label="キャラのシステムプロンプト", lines=4)
125
+
126
+ with gr.Row():
127
+ temperature = gr.Slider(0.0, 1.0, value=0.7, label="Temperature")
128
+ top_p = gr.Slider(0.0, 1.0, value=0.9, label="Top-p")
129
+ top_k = gr.Slider(1, 100, value=40, label="Top-k")
130
+ max_output_tokens = gr.Number(value=1024, label="Max Output Tokens", precision=0)
131
+
132
+ msg.submit(generate_response,
133
+ inputs=[msg, state, temperature, top_p, top_k, max_output_tokens, system_prompt],
134
+ outputs=[msg, chatbot, state])
135
+
136
+ with gr.Tab("キャラクター投稿"):
137
+ char_name = gr.Textbox(label="キャラクター名")
138
+ char_prompt = gr.Textbox(label="システムプロンプト", lines=5)
139
+ submit_char = gr.Button("キャラクターを追加")
140
+ char_status = gr.Textbox(label="ステータス", interactive=False)
141
+
142
+ def post_character(name, prompt):
143
+ try:
144
+ upload_character(name, prompt)
145
+ return "キャラクターをアップロードしました。"
146
+ except Exception as e:
147
+ return f"失敗しました: {e}"
148
+
149
+ submit_char.click(post_character, inputs=[char_name, char_prompt], outputs=[char_status])
150
+
151
+ with gr.Tab("キャラクター選択"):
152
+ character_list = gr.Dropdown(choices=[], label="使用するキャラクターを選択")
153
+
154
+ def refresh_characters():
155
+ return gr.update(choices=[c["name"] for c in fetch_characters()])
156
+
157
+ def load_prompt(name):
158
+ for c in fetch_characters():
159
+ if c.get("name") == name:
160
+ print(f"[DEBUG] 読み込み成功: {c}")
161
+ return c.get("prompt", "")
162
+ print("[DEBUG] キャラが見つかりません")
163
+ return ""
164
+
165
+ character_list.change(load_prompt, inputs=[character_list], outputs=[system_prompt])
166
+ demo.load(refresh_characters, outputs=[character_list])
167
+
168
+ # ドロップダウンでテーマ切り替え
169
+ theme_dropdown.change(
170
+ fn=switch_theme,
171
+ inputs=[theme_dropdown],
172
+ outputs=[gr.HTML(), gr.Textbox(), gr.HTML()],
173
+ _js="(theme) => { return theme; }"
174
+ )
175
+
176
+ demo.launch()