Spaces:
Sleeping
Sleeping
File size: 9,087 Bytes
45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 146c49f 45b631d 146c49f 85c0ea4 146c49f 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 146c49f 45b631d 146c49f 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 b00b12d 85c0ea4 b00b12d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d 85c0ea4 45b631d | 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 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | import gradio as gr
import uuid
import os
import csv
import datetime
from datetime import timezone, timedelta
import re
from openai import OpenAI
import sys
# --- JST(日本時間)の定義 ---
JST = timezone(timedelta(hours=9), 'JST')
# --- API Key and Client Initialization ---
OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY")
if not OPENROUTER_API_KEY:
print("Warning: OpenRouter API Key not found.", file=sys.stderr)
try:
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=OPENROUTER_API_KEY,
)
print("OpenAI client initialized.")
except Exception as e:
print(f"Failed to initialize OpenAI client: {e}", file=sys.stderr)
# --- Helper Functions ---
def generate_user_id():
return str(uuid.uuid4())
def load_prompt(level):
try:
filename = f"{level.lower()}_prompt.txt"
if os.path.exists(filename):
with open(filename, "r", encoding="utf-8") as f:
return f.read()
return "You are a helpful AI assistant."
except Exception:
return "You are a helpful AI assistant."
# --- Core Logic Functions ---
def chat_process(message, display_history, full_logs, prompt_level):
"""
チャット処理を行う関数
"""
if display_history is None: display_history = []
if full_logs is None: full_logs = []
# 1. ユーザーメッセージの作成
current_time_user = datetime.datetime.now(JST).strftime("%Y-%m-%d %H:%M:%S")
user_msg_data = {
"role": "user",
"content": message,
"timestamp": current_time_user
}
temp_display = display_history + [user_msg_data]
temp_logs = full_logs + [user_msg_data]
# APIリクエスト用のメッセージ作成
system_prompt = load_prompt(prompt_level)
messages_for_api = [{"role": "system", "content": system_prompt}]
for msg in temp_display:
messages_for_api.append({"role": msg["role"], "content": msg["content"]})
ai_response = ""
try:
model_name = "google/gemini-2.5-flash"
response = client.chat.completions.create(
model=model_name,
messages=messages_for_api,
temperature=0.7,
max_tokens=500,
)
content = response.choices[0].message.content
ai_response = content if content is not None else ""
if not ai_response:
ai_response = "(応答が空でした)"
except Exception as e:
error_msg = f"API Error: {e}"
print(error_msg, file=sys.stderr)
ai_response = f"システムエラーが発生しました: {e}"
# 2. AIメッセージの作成
current_time_ai = datetime.datetime.now(JST).strftime("%Y-%m-%d %H:%M:%S")
ai_msg_data = {
"role": "assistant",
"content": ai_response,
"timestamp": current_time_ai
}
new_display = temp_display + [ai_msg_data]
new_logs = temp_logs + [ai_msg_data]
return "", new_display, new_display, new_logs
def change_difficulty_logic(new_level, full_logs):
"""
難易度変更時の処理
"""
if full_logs is None: full_logs = []
notification_msg = (
f"**[システム通知]**\n"
f"難易度を **{new_level}** に変更しました。\n"
f"解答開始と入力すれば指導が始まります。"
)
current_time = datetime.datetime.now(JST).strftime("%Y-%m-%d %H:%M:%S")
msg_data = {
"role": "assistant",
"content": notification_msg,
"timestamp": current_time
}
new_display_history = [msg_data]
new_full_logs = full_logs + [msg_data]
return new_display_history, new_display_history, new_full_logs, new_level
def export_csv_logic(user_id, user_name, full_logs):
print(f"Exporting CSV for UserID: {user_id}, Name: '{user_name}'")
if not user_name or not user_name.strip():
gr.Warning("名前が正しく取得できませんでした")
return None
if not full_logs:
gr.Warning("保存する履歴がまだありません。")
return None
timestamp_str = datetime.datetime.now(JST).strftime("%Y%m%d_%H%M%S")
safe_name = re.sub(r'[\\/*?:"<>|]', "", user_name)
safe_name = safe_name.strip().replace(" ", "_")
filename = f"chat_history_{safe_name}_{timestamp_str}.csv"
try:
with open(filename, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerow(["Timestamp", "Role", "Content"])
for msg in full_logs:
ts = msg.get("timestamp", "")
role = msg.get("role", "")
content = msg.get("content", "")
writer.writerow([ts, role, content])
print(f"Successfully saved to {filename}")
return filename
except Exception as e:
print(f"CSV Export Error: {e}", file=sys.stderr)
gr.Warning(f"CSV保存エラー: {e}")
return None
def start_app_logic(user_name):
"""
名前が入力されているか確認し、画面表示を切り替える
"""
if not user_name or not user_name.strip():
raise gr.Error("お名前を入力してください。")
# Start画面を隠し(False)、Main画面を表示(True)
return gr.update(visible=False), gr.update(visible=True)
# --- UI Definition ---
with gr.Blocks() as demo:
# ステート定義
user_id_state = gr.State(generate_user_id)
display_history_state = gr.State([]) # 画面用
full_logs_state = gr.State([]) # 保存用
prompt_level_state = gr.State("Beginner")
start_msg_constant = gr.State("解答開始")
# =========================================
# 1. スタート画面
# =========================================
with gr.Column(visible=True) as start_screen:
gr.Markdown("# AI Chatbot System")
gr.Markdown("学習を始めるには、お名前を入力して「解答開始」を押してください。")
user_name_input = gr.Textbox(
label="お名前",
placeholder="山田 太郎",
scale=2
)
start_btn = gr.Button("解答開始", variant="primary", size="lg")
# =========================================
# 2. メインチャット画面
# =========================================
with gr.Column(visible=False) as main_screen:
gr.Markdown("# Multi-Level AI Chatbot")
with gr.Row():
export_btn = gr.DownloadButton("📥 会話をCSVで保存", scale=1)
with gr.Row():
gr.Markdown("## 問題の難易度を選択してください(初級から順番に取り組んでください)")
with gr.Row():
level_radio = gr.Radio(
["Beginner", "Intermediate", "Advanced"],
label="Difficulty Level",
value="Beginner",
interactive=True
)
# 【修正箇所】Chatbotの定義にlatex_delimitersを追加
chatbot = gr.Chatbot(
type="messages",
height=500,
latex_delimiters=[
{"left": "$$", "right": "$$", "display": True}, # 行全体(ディスプレイ数式)
{"left": "$", "right": "$", "display": False}, # インライン数式
{"left": "\\(", "right": "\\)", "display": False},
{"left": "\\[", "right": "\\]", "display": True},
]
)
msg_input = gr.Textbox(
placeholder="メッセージを入力してください... (Enterで送信)",
label="Chat Input",
lines=1
)
send_btn = gr.Button("送信", variant="primary")
# --- Event Handling ---
# A. スタートボタンの処理
start_btn.click(
fn=start_app_logic,
inputs=[user_name_input],
outputs=[start_screen, main_screen]
).then(
fn=chat_process,
inputs=[start_msg_constant, display_history_state, full_logs_state, prompt_level_state],
outputs=[msg_input, chatbot, display_history_state, full_logs_state]
)
# B. メッセージ送信時の処理
chat_event_args = {
"fn": chat_process,
"inputs": [msg_input, display_history_state, full_logs_state, prompt_level_state],
"outputs": [msg_input, chatbot, display_history_state, full_logs_state]
}
msg_input.submit(**chat_event_args)
send_btn.click(**chat_event_args)
# C. 難易度変更時の処理
level_radio.change(
fn=change_difficulty_logic,
inputs=[level_radio, full_logs_state],
outputs=[chatbot, display_history_state, full_logs_state, prompt_level_state]
)
# D. CSV保存処理
export_btn.click(
fn=export_csv_logic,
inputs=[user_id_state, user_name_input, full_logs_state],
outputs=[export_btn]
)
if __name__ == "__main__":
demo.launch(debug=True, share=False) |