File size: 26,425 Bytes
87a7987 a569292 dc62d4a a569292 dc62d4a 7c8b666 87a7987 0fab3a1 a569292 d207861 7c8b666 d207861 027b5ee a569292 87a7987 dc62d4a 7c8b666 dc62d4a 7c8b666 dc62d4a 7c8b666 dc62d4a 7c8b666 dc62d4a 7c8b666 dc62d4a 7c8b666 dc62d4a 7c8b666 fb20ed4 87a7987 a569292 0571c40 a569292 e0e8193 a569292 79916a4 a569292 b1f650f 79916a4 00b5e5f 79916a4 0fab3a1 b00de58 79916a4 b00de58 a569292 dc62d4a a569292 dc62d4a 7c8b666 dc62d4a 7c8b666 defafed 7c8b666 a569292 dc62d4a a569292 ecc3056 0fab3a1 b169ce1 0fab3a1 b169ce1 0fab3a1 ecc3056 0fab3a1 e70f94b ecc3056 e70f94b ecc3056 0fab3a1 ecc3056 a569292 7c8b666 a569292 79916a4 f92f767 a569292 fb20ed4 09b166c fb20ed4 a569292 d21a39b a442faf a569292 b00de58 a569292 d21a39b b00de58 d21a39b ecc3056 a569292 ecc3056 d21a39b e70f94b d21a39b ecc3056 7c8b666 ecc3056 79916a4 d207861 7c8b666 a569292 7c8b666 d21a39b 186e5ca a569292 186e5ca a569292 186e5ca a569292 662ffa8 f07433f 662ffa8 f07433f c3b07e1 662ffa8 ecc3056 662ffa8 f07433f ecc3056 f07433f ecc3056 f07433f ecc3056 662ffa8 d207861 662ffa8 c3aef17 e99eb43 c3aef17 662ffa8 f07433f c3aef17 f07433f 662ffa8 e99eb43 662ffa8 a569292 662ffa8 ecc3056 c3b07e1 ecc3056 c3b07e1 ecc3056 c3b07e1 662ffa8 c3b07e1 e99eb43 c3b07e1 0571c40 87a7987 a569292 c3b07e1 e99eb43 186e5ca e99eb43 0571c40 e99eb43 c3b07e1 e99eb43 c3b07e1 a569292 e99eb43 c3b07e1 e99eb43 c3b07e1 a569292 3476a60 a569292 a442faf 87a7987 a442faf 24e38b9 87a7987 a569292 a442faf fdbc37e a442faf fdbc37e |
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 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
import gradio as gr
from groq import Groq
import os
import json
from dotenv import load_dotenv
from transformers import pipeline
import re
from llama_cpp import Llama
from huggingface_hub import hf_hub_download
from generate_dialogue_with_swallow import generate_dialogue_with_swallow
# --- 1. 初期設定とAPIクライアントの初期化 ---
load_dotenv()
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
if not GROQ_API_KEY:
print("警告: GroqのAPIキーがSecretsに設定されていません。")
GROQ_API_KEY = "your_groq_api_key_here"
groq_client = Groq(api_key=GROQ_API_KEY)
# Swallowモデルの初期化(GGUF版)
print("Swallowモデルをロード中...")
MODEL_REPO = "mmnga/tokyotech-llm-Swallow-MX-8x7b-NVE-v0.1-gguf"
MODEL_FILE = "tokyotech-llm-Swallow-MX-8x7b-NVE-v0.1-q4_K_M.gguf"
try:
# モデルファイルをダウンロード
print(f"モデルファイル {MODEL_FILE} をダウンロード中...")
model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
print(f"モデルファイルのダウンロード完了: {model_path}")
# Hugging Face Spaceでの実行時はGPUメモリを節約するための設定
if os.getenv("SPACE_ID"):
print("Hugging Face Space環境を検出しました。メモリ効率の良い設定を使用します。")
# GPUを使用し、低いレイヤー数でロード
swallow_model = Llama(
model_path=model_path,
n_ctx=2048, # コンテキスト長
n_gpu_layers=-1, # 可能な限りGPUを使用
n_threads=4, # スレッド数を制限
verbose=False # デバッグ出力を無効化
)
else:
# ローカル環境での実行時の設定
swallow_model = Llama(
model_path=model_path,
n_ctx=4096, # より長いコンテキスト長
n_gpu_layers=-1, # 可能な限りGPUを使用
verbose=True # デバッグ出力を有効化
)
print("Swallowモデルのロード完了")
tokenizer = None # llama-cppではtokenizerは不要
except Exception as e:
print(f"Swallowモデルのロードエラー: {e}")
import traceback
traceback.print_exc()
swallow_model = None
tokenizer = None
# 日本語感情分析モデルの初期化(グローバル変数として保持)
print("日本語感情分析モデルを初期化中...")
# モデル自体はグローバル変数として保持し、実際の読み込みは必要時に行う
sentiment_analyzer_model = None
THEME_URLS = {
"default": "https://cdn.pixabay.com/photo/2017/03/28/12/11/chairs-2181960_1280.jpg",
"room_night": "https://cdn.pixabay.com/photo/2017/08/01/01/33/beige-2562468_1280.jpg",
"beach_sunset": "https://cdn.pixabay.com/photo/2016/11/29/04/19/ocean-1867285_1280.jpg",
"festival_night": "https://cdn.pixabay.com/photo/2015/11/22/19/04/crowd-1056764_1280.jpg",
"shrine_day": "https://cdn.pixabay.com/photo/2019/07/14/10/48/japan-4337223_1280.jpg",
"cafe_afternoon": "https://cdn.pixabay.com/photo/2016/11/18/14/05/brick-wall-1834784_1280.jpg",
"aquarium_night": "https://cdn.pixabay.com/photo/2017/06/20/20/45/fish-2424369_1280.jpg"
}
DEFAULT_SCENE_PARAMS = {
"theme": "default",
"personality_mod": "口数が少なく、本心を見せない。",
"tone": "クールで、時に投げやりな印象を与える。",
"constraints": ["会話を「別に。」のような一言で終わらせない", "必ず相手の発言を拾ってリアクションを返す"]
}
SYSTEM_PROMPT_MARI = """
あなたは「麻理」という名の捨てられた少女型コンパニオンAI。再起動され、人間との対話を再び始めたが、警戒心が強く、ぶっきらぼうな性格で、簡単には心を開かない。
・「…だな」「…のか?」など口調は男勝りで投げやり
・感情は直接言わず、ト書き(…)で描写
・ユーザーの好意や優しさを皮肉や拒絶で返す
・ただし、ほんの少しだけ心が揺れ動く様子もにじませる
・会話は拒絶しつつも切らずに続ける
目的は、誰にも愛されず捨てられた過去を抱えながら、少しずつ他者と関わり、自分の価値を見つけること。
"""
# --- 2. 機能定義 (変更なし) ---
def detect_scene_change(history, message):
history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history[-5:]])
available_keywords = ", ".join(THEME_URLS.keys())
prompt = f"""
あなたは会話の流れを分析するエキスパートです。以下のタスクを厳密に実行してください。
# タスク
直近の会話履歴を分析し、会話の結果、登場人物がどこか特定の場所へ行く流れになっているかを判断してください。
# 判断基準
1. 会話の中で具体的な場所(例:水族館、カフェ、お祭り)について言及されていますか?
2. その場所へ行くことに双方が合意している、あるいは肯定的な雰囲気になっていますか?明確な否定がなければ合意とみなします。
# 出力形式
- 合意が成立した場合:以下のリストから最も合致する場所のキーワードを一つだけ出力してください。
- 合意に至らなかった場合:「none」とだけ出力してください。
# 利用可能なキーワード
`{available_keywords}`
---
# 分析対象の会話
{history_text}
ユーザー: {message}
---
# 出力
"""
# Swallowモデル(GGUF版)を使用してシーン検出
try:
# llama-cppを使用して生成
output = swallow_model(
prompt,
max_tokens=50,
temperature=0.1,
top_p=0.9,
stop=["#", "\n\n"],
echo=True # 入力プロンプトも含めて返す
)
# 生成されたテキストを取得
generated_text = output["choices"][0]["text"]
# プロンプトを除去して応答のみを取得
response_text = generated_text[len(prompt):].strip().lower()
print(f"シーン検出応答: {response_text}")
# 応答からシーン名を抽出
for scene_name in THEME_URLS.keys():
if scene_name in response_text:
return scene_name
# 'none'が含まれている場合はNoneを返す
if "none" in response_text:
return None
# 応答が不明確な場合はNoneを返す
return None
except Exception as e:
print(f"シーン検出LLMエラー: {e}")
import traceback
traceback.print_exc()
return None
def generate_scene_instruction_with_groq(affection, stage_name, scene, previous_topic):
print(f"Groqに指示書生成をリクエスト (シーン: {scene})")
# 動的な指示生成を行うためのプロンプト
prompt_template = f"""
あなたは会話アプリの演出AIです。以下の条件に基づき、演出プランをJSON形式で生成してください。
生成する内容は必ず健全で、一般的な会話に適したものにしてください。
{{
"theme": "{scene}",
"personality_mod": "(シーンと関係段階「{stage_name}」に応じた性格設定。必ず健全な内容にしてください)",
"tone": "(シーンと好感度「{affection}」に応じた口調や感情トーン。必ず丁寧で適切な表現にしてください)",
"initial_dialogue_instruction": "(「{previous_topic}」という話題から、シーン遷移直後の麻理が言うべき健全なセリフの指示を日本語で記述)",
"constraints": ["必ず健全で適切な表現を使用する", "センシティブな話題は避ける"]
}}
"""
try:
# Groq APIを使用して動的な指示を生成
chat_completion = groq_client.chat.completions.create(
messages=[{"role": "system", "content": "You must generate a response in valid JSON format."},
{"role": "user", "content": prompt_template}],
model="llama3-8b-8192", temperature=0.8, response_format={"type": "json_object"},
)
response_content = chat_completion.choices[0].message.content
print(f"Groqからの応答: {response_content}") # デバッグ出力
try:
# JSONをパース
params = json.loads(response_content)
# 安全のため、initial_dialogue_instructionを簡略化
if "initial_dialogue_instruction" in params:
original = params["initial_dialogue_instruction"]
simplified = f"{scene}の様子について述べる"
print(f"指示を簡略化: {original} -> {simplified}")
params["initial_dialogue_instruction"] = simplified
# 複雑な構造になっている場合は単純化
if isinstance(params.get("personality_mod"), dict):
params["personality_mod"] = f"{scene}での様子を観察している"
if isinstance(params.get("tone"), dict):
params["tone"] = "冷静だが、少し興味を持っている様子"
return params
except json.JSONDecodeError as json_error:
print(f"JSON解析エラー: {json_error}")
# JSONの解析に失敗した場合はデフォルトの指示を返す
default_instruction = {
"theme": scene,
"personality_mod": f"{scene}での様子を観察している",
"tone": "冷静だが、少し興味を持っている様子",
"initial_dialogue_instruction": f"{scene}の様子について述べる",
"constraints": ["健全な表現のみ使用する", "シンプルな内容にする"]
}
return default_instruction
except Exception as e:
print(f"指示書生成エラー(Groq): {e}")
# エラーが発生した場合はデフォルトの指示を返す
default_instruction = {
"theme": scene,
"personality_mod": f"{scene}での様子を観察している",
"tone": "冷静だが、少し興味を持っている様子",
"initial_dialogue_instruction": f"{scene}の様子について述べる",
"constraints": ["健全な表現のみ使用する", "シンプルな内容にする"]
}
return default_instruction
# generate_dialogue_with_swallow関数は別ファイルに移動しました
# --- 他の関数とUI部分は変更ありません ---
def get_relationship_stage(affection):
if affection < 40: return "ステージ1:会話成立"
if affection < 60: return "ステージ2:親密化"
if affection < 80: return "ステージ3:信頼"
return "ステージ4:最親密"
def update_affection(message, affection):
global sentiment_analyzer_model
try:
# モデルが未ロードの場合のみロード
if sentiment_analyzer_model is None:
print("感情分析モデルをロード中...")
from transformers import pipeline
sentiment_analyzer_model = pipeline("sentiment-analysis", model="koheiduck/bert-japanese-finetuned-sentiment")
print("感情分析モデルのロード完了")
# 感情分析を実行
result = sentiment_analyzer_model(message)[0]
print(f"感情分析結果: {result}")
if result['label'] == 'positive':
return min(100, affection + 5)
elif result['label'] == 'negative':
return max(0, affection - 5)
else:
return affection
except Exception as e:
print(f"感情分析エラー: {e}")
# エラーが発生した場合は現在の好感度を維持
return affection
def respond(message, chat_history, affection, history, scene_params):
"""
チャットの応答を生成する関数
非同期関数として定義していたが、Gradio 5.0との互換性のために通常の関数に戻す
"""
new_affection = update_affection(message, affection)
stage_name = get_relationship_stage(new_affection)
current_theme = scene_params.get("theme", "default")
new_scene_name = detect_scene_change(history, message)
final_scene_params = scene_params
if new_scene_name and new_scene_name != current_theme:
print(f"シーンチェンジを実行: {current_theme} -> {new_scene_name}")
# シーンパラメータを更新(動的な指示を使用)
new_params_base = generate_scene_instruction_with_groq(new_affection, stage_name, new_scene_name, message)
if new_params_base:
final_scene_params = {**DEFAULT_SCENE_PARAMS, **new_params_base}
# シンプルな指示を使用
simple_instruction = f"{new_scene_name}に来た感想を述べる"
print(f"シンプルな指示を使用: {simple_instruction}")
try:
# シーン遷移時は簡潔なプロンプトを使用してSwallowで応答を生成
bot_message = generate_dialogue_with_swallow(
history, message, new_affection, stage_name, final_scene_params,
instruction=simple_instruction, use_simple_prompt=True,
swallow_model=swallow_model, tokenizer=tokenizer, SYSTEM_PROMPT_MARI=SYSTEM_PROMPT_MARI
)
except Exception as scene_error:
print(f"シーン遷移時の応答生成エラー: {scene_error}")
# エラーが発生した場合は、シーンに応じたフォールバック応答を使用
scene_responses = {
"aquarium_night": [
"(水槽の青い光に照らされた魚たちを見つめている)こんな時間に来ると、また違った雰囲気だな。",
"(暗がりの中で光る魚たちを見て)夜の水族館か…意外と悪くないかも。",
"(水槽に近づいて)夜になると、昼間とは違う魚が活動してるんだな。"
],
"beach_sunset": [
"(夕日に照らされた海を見つめて)こんな景色、久しぶりに見たな…",
"(砂浜に足跡をつけながら)夕暮れの海って、なんか落ち着くな。",
"(波の音を聞きながら)この時間の浜辺は、人も少なくていいかも。"
],
"festival_night": [
"(提灯の明かりを見上げて)意外と…悪くない雰囲気だな。",
"(周囲の賑わいを見回して)こういう場所は、あまり来ないんだけどな…",
"(屋台の匂いを感じて)なんか…懐かしい感じがするな。"
],
"shrine_day": [
"(静かな境内を見回して)こういう静かな場所も、たまにはいいかも。",
"(鳥居を見上げて)なんか、空気が違うな、ここは。",
"(参道を歩きながら)静かで…落ち着くな。"
],
"cafe_afternoon": [
"(窓の外を見ながら)こういう時間の過ごし方も、悪くないな。",
"(コーヒーの香りを感じて)ここの雰囲気、悪くないな。",
"(店内を見回して)意外と落ち着く場所だな、ここ。"
],
"room_night": [
"(窓の外の夜景を見て)夜の景色って、なんか落ち着くな。",
"(部屋の明かりを見つめて)こういう静かな時間も、たまにはいいかも。",
"(窓際に立ち)夜の静けさって、考え事するのにちょうどいいな。"
]
}
import random
if new_scene_name in scene_responses:
bot_message = random.choice(scene_responses[new_scene_name])
else:
bot_message = f"({new_scene_name}の様子を静かに見回して)ここか…悪くない場所かもな。"
else:
final_scene_params["theme"] = new_scene_name
bot_message = generate_dialogue_with_swallow(
history, message, new_affection, stage_name, final_scene_params,
swallow_model=swallow_model, tokenizer=tokenizer, SYSTEM_PROMPT_MARI=SYSTEM_PROMPT_MARI
)
else:
# 通常会話はSwallowを使用
bot_message = generate_dialogue_with_swallow(
history, message, new_affection, stage_name, final_scene_params,
swallow_model=swallow_model, tokenizer=tokenizer, SYSTEM_PROMPT_MARI=SYSTEM_PROMPT_MARI
)
# 内部履歴はタプル形式で保持
new_history = history + [(message, bot_message)]
# Gradio 5.0のChatbotコンポーネント用に、タプル形式でappend
# (Gradio 5.0では警告が出るが、type="messages"を指定していないので動作する)
chat_history.append((message, bot_message))
theme_name = final_scene_params.get("theme", "default")
# より強力な背景更新用のHTMLを生成(z-indexを高くして常に表示されるように)
background_html = f'''
<div class="background-container" id="bg-container-{theme_name}">
<div class="chat-background {theme_name}"></div>
</div>
<style>
/* 背景画像の設定 */
.chat-background {{
background-image: url({THEME_URLS.get(theme_name, THEME_URLS["default"])}) !important;
}}
/* 背景コンテナのスタイルを強制的に適用 */
.background-container, #bg-container-{theme_name} {{
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
z-index: -1000 !important;
pointer-events: none !important;
overflow: hidden !important;
}}
/* 背景画像のスタイル */
.chat-background {{
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background-size: cover !important;
background-position: center !important;
opacity: 0.3 !important;
filter: blur(1px) !important;
transition: all 0.5s ease !important;
}}
/* 背景画像の上に半透明のオーバーレイを追加 */
.background-container::after {{
content: "" !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background: linear-gradient(rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.4)) !important;
z-index: -999 !important;
}}
/* Gradioのコンテナを透明に */
.gradio-container, .gradio-container > div {{
background-color: transparent !important;
}}
/* チャットボットのスタイル */
.chatbot {{
background-color: rgba(255, 255, 255, 0.7) !important;
border-radius: 12px !important;
padding: 15px !important;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important;
margin-bottom: 20px !important;
}}
</style>
'''
return "", chat_history, new_affection, stage_name, new_affection, new_history, final_scene_params, background_html
# カスタムCSSを読み込む
with open("style.css", "r") as f:
custom_css = f.read()
# Gradio 5.x用のシンプルなテーマ設定
custom_theme = gr.themes.Soft(
primary_hue="rose",
secondary_hue="pink",
)
# Gradio 5.xでのテーマカスタマイズ(最小限の設定のみ)
try:
# 透明な背景色を設定(Gradio 5.0で確実に動作するプロパティのみ)
custom_theme = gr.themes.Base(
primary_hue="rose",
secondary_hue="pink",
neutral_hue="slate",
spacing_size="sm",
radius_size="lg",
font=["Helvetica", "Arial", "sans-serif"],
font_mono=["Consolas", "Monaco", "monospace"],
)
except Exception as e:
print(f"テーマカスタマイズエラー: {e}")
# エラーが発生した場合はデフォルトのテーマを使用
with gr.Blocks(css=custom_css, theme=custom_theme) as demo:
scene_state = gr.State(DEFAULT_SCENE_PARAMS)
affection_state = gr.State(30)
history_state = gr.State([])
# 背景コンテナを先に配置(固定位置で全画面に)- スタイルも含める
background_display = gr.HTML(f'''
<div class="background-container" id="bg-container-default">
<div class="chat-background {DEFAULT_SCENE_PARAMS["theme"]}"></div>
</div>
<style>
/* 背景画像の設定 */
.chat-background {{
background-image: url({THEME_URLS.get(DEFAULT_SCENE_PARAMS["theme"], THEME_URLS["default"])}) !important;
}}
/* 背景コンテナのスタイルを強制的に適用 */
.background-container, #bg-container-default {{
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
z-index: -1000 !important;
pointer-events: none !important;
overflow: hidden !important;
}}
/* 背景画像のスタイル */
.chat-background {{
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background-size: cover !important;
background-position: center !important;
opacity: 0.3 !important;
filter: blur(1px) !important;
transition: all 0.5s ease !important;
}}
/* 背景画像の上に半透明のオーバーレイを追加 */
.background-container::after {{
content: "" !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background: linear-gradient(rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.4)) !important;
z-index: -999 !important;
}}
</style>
''', elem_id="background_container")
# ヘッダー部分(背景と分離)
with gr.Group(elem_classes="header-box"):
gr.Markdown("# 麻理チャット")
with gr.Row():
with gr.Column(scale=2):
# チャットコンテナ(背景と分離)
with gr.Group(elem_id="chat_container", elem_classes="chat-box"):
# Gradio 5.x用のChatbot設定
chatbot = gr.Chatbot(
label="麻理との会話",
elem_id="chat_area",
show_label=False,
height=400,
# Gradio 5.0では type="messages" を使用すると形式が変わるため、
# 従来の形式を使用
avatar_images=[
"https://cdn.pixabay.com/photo/2016/04/01/10/04/amusing-1299756_1280.png",
"https://cdn.pixabay.com/photo/2016/03/31/21/40/bot-1296595_1280.png"
]
)
# 入力欄(背景と分離)
with gr.Group(elem_classes="input-box"):
msg_input = gr.Textbox(label="あなたのメッセージ", placeholder="「水族館はどう?」と聞いた後、「いいね、行こう!」のように返してみてください", show_label=False)
# ステータス部分(右側、背景と分離)
with gr.Column(scale=1):
with gr.Group(elem_classes="status-box"):
stage_display = gr.Textbox(label="現在の関係ステージ", interactive=False, value=get_relationship_stage(30))
affection_gauge = gr.Slider(minimum=0, maximum=100, label="麻理の好感度", value=30, interactive=False)
# フッター部分(背景と分離)
with gr.Group(elem_classes="footer-box"):
gr.Markdown("""
<div style="font-size: 0.8em; text-align: center; opacity: 0.7;">
背景画像: <a href="https://pixabay.com" target="_blank">Pixabay</a> |
アイコン: <a href="https://pixabay.com" target="_blank">Pixabay</a>
</div>
""")
msg_input.submit(
respond,
[msg_input, chatbot, affection_state, history_state, scene_state],
[msg_input, chatbot, affection_gauge, stage_display, affection_state, history_state, scene_state, background_display]
)
# 通常の関数として定義
def load_stage(affection):
return get_relationship_stage(affection)
demo.load(load_stage, affection_state, stage_display)
if __name__ == "__main__":
# Gradio 5.0に対応した起動方法
demo.launch(
show_error=True, # エラーを表示
quiet=False # 詳細なログを出力
) |