sirochild commited on
Commit
ecc3056
·
verified ·
1 Parent(s): a442faf

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +202 -38
app.py CHANGED
@@ -97,6 +97,8 @@ def detect_scene_change(history, message):
97
 
98
  def generate_scene_instruction_with_groq(affection, stage_name, scene, previous_topic):
99
  print(f"Groqに指示書生成をリクエスト (シーン: {scene})")
 
 
100
  prompt_template = f"""
101
  あなたは会話アプリの演出AIです。以下の条件に基づき、演出プランをJSON形式で生成してください。
102
  生成する内容は必ず健全で、一般的な会話に適したものにしてください。
@@ -110,6 +112,7 @@ def generate_scene_instruction_with_groq(affection, stage_name, scene, previous_
110
  }}
111
  """
112
  try:
 
113
  chat_completion = groq_client.chat.completions.create(
114
  messages=[{"role": "system", "content": "You must generate a response in valid JSON format."},
115
  {"role": "user", "content": prompt_template}],
@@ -117,21 +120,54 @@ def generate_scene_instruction_with_groq(affection, stage_name, scene, previous_
117
  )
118
  response_content = chat_completion.choices[0].message.content
119
  print(f"Groqからの応答: {response_content}") # デバッグ出力
120
- params = json.loads(response_content)
121
 
122
- # 安全のため、initial_dialogue_instructionを簡略化
123
- if "initial_dialogue_instruction" in params:
124
- original = params["initial_dialogue_instruction"]
125
- simplified = f"{scene}に来た感想を述べる"
126
- print(f"指示を簡略化: {original} -> {simplified}")
127
- params["initial_dialogue_instruction"] = simplified
 
 
 
 
 
 
 
 
128
 
129
- return params
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  except Exception as e:
131
  print(f"指示書生成エラー(Groq): {e}")
132
- return None
 
 
 
 
 
 
 
 
133
 
134
  def generate_dialogue_with_gemini(history, message, affection, stage_name, scene_params, instruction=None, use_simple_prompt=False):
 
 
 
 
135
  history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history])
136
  task_prompt = f"指示: {instruction}" if instruction else f"ユーザー: {message}"
137
 
@@ -175,12 +211,18 @@ def generate_dialogue_with_gemini(history, message, affection, stage_name, scene
175
  try:
176
  # デバッグ情報を追加
177
  print(f"Gemini API呼び出し開始...")
 
178
 
179
- generation_config = genai.types.GenerationConfig(max_output_tokens=200, temperature=0.95)
180
- response = gemini_model.generate_content(
181
- system_prompt,
182
- generation_config=generation_config
183
- )
 
 
 
 
 
184
 
185
  print(f"Gemini API呼び出し完了")
186
  print(f"応答候補数: {len(response.candidates) if hasattr(response, 'candidates') and response.candidates else 'なし'}")
@@ -197,12 +239,22 @@ def generate_dialogue_with_gemini(history, message, affection, stage_name, scene
197
 
198
  # 正常に応答が返されたかどうかを厳密にチェック
199
  if (response.candidates and
200
- response.candidates[0].finish_reason in {1, 'STOP'} and
201
- hasattr(response, 'text') and
202
- response.text is not None):
203
 
204
- print(f"正常応答: {response.text.strip()}")
205
- return response.text.strip()
 
 
 
 
 
 
 
 
 
 
 
 
206
  else:
207
  print(f"異常応答検出")
208
  # エラー情報をコンソールに出力(デバッグ用)
@@ -358,29 +410,60 @@ def respond(message, chat_history, affection, history, scene_params):
358
  if new_scene_name and new_scene_name != current_theme:
359
  print(f"シーンチェンジを実行: {current_theme} -> {new_scene_name}")
360
 
361
- # シーンパラメータを更新
362
  new_params_base = generate_scene_instruction_with_groq(new_affection, stage_name, new_scene_name, message)
363
  if new_params_base:
364
- # Groqからの応答を精査し、問題のある可能性のあるフィールドを修正
365
- if isinstance(new_params_base.get("personality_mod"), dict):
366
- # 複雑な構造になっている場合は単純化
367
- new_params_base["personality_mod"] = f"{new_scene_name}での様子を観察している"
368
-
369
- if isinstance(new_params_base.get("tone"), dict):
370
- # 複雑な構造になっている場合は単純化
371
- new_params_base["tone"] = "冷静だが、少し興味を持っている様子"
372
-
373
- # 英語の指示を日本語に変換
374
- if "initial_dialogue_instruction" in new_params_base and new_params_base["initial_dialogue_instruction"].strip().lower().startswith("so"):
375
- new_params_base["initial_dialogue_instruction"] = f"{new_scene_name}の感想を述べる"
376
-
377
  final_scene_params = {**DEFAULT_SCENE_PARAMS, **new_params_base}
 
378
  # シンプルな指示を使用
379
  simple_instruction = f"{new_scene_name}に来た感想を述べる"
380
  print(f"シンプルな指示を使用: {simple_instruction}")
381
 
382
- # シーン遷移時は簡潔なプロンプトを使用してGeminiで応答を生成
383
- bot_message = generate_dialogue_with_gemini(history, message, new_affection, stage_name, final_scene_params, instruction=simple_instruction, use_simple_prompt=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
  else:
385
  final_scene_params["theme"] = new_scene_name
386
  bot_message = generate_dialogue_with_gemini(history, message, new_affection, stage_name, final_scene_params)
@@ -403,12 +486,13 @@ def respond(message, chat_history, affection, history, scene_params):
403
  <div class="chat-background {theme_name}"></div>
404
  </div>
405
  <style>
 
406
  .chat-background {{
407
  background-image: url({THEME_URLS.get(theme_name, THEME_URLS["default"])}) !important;
408
  }}
409
 
410
  /* 背景コンテナのスタイルを強制的に適用 */
411
- #bg-container-{theme_name} {{
412
  position: fixed !important;
413
  top: 0 !important;
414
  left: 0 !important;
@@ -416,12 +500,48 @@ def respond(message, chat_history, affection, history, scene_params):
416
  height: 100% !important;
417
  z-index: -1000 !important;
418
  pointer-events: none !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  }}
420
 
421
  /* Gradioのコンテナを透明に */
422
  .gradio-container, .gradio-container > div {{
423
  background-color: transparent !important;
424
  }}
 
 
 
 
 
 
 
 
 
425
  </style>
426
  '''
427
 
@@ -458,11 +578,55 @@ with gr.Blocks(css=custom_css, theme=custom_theme) as demo:
458
  affection_state = gr.State(30)
459
  history_state = gr.State([])
460
 
461
- # 背景コンテナを先に配置(固定位置で全画面に)
462
  background_display = gr.HTML(f'''
463
- <div class="background-container">
464
  <div class="chat-background {DEFAULT_SCENE_PARAMS["theme"]}"></div>
465
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  ''', elem_id="background_container")
467
 
468
  # ヘッダー部分(背景と分離)
 
97
 
98
  def generate_scene_instruction_with_groq(affection, stage_name, scene, previous_topic):
99
  print(f"Groqに指示書生成をリクエスト (シーン: {scene})")
100
+
101
+ # 動的な指示生成を行うためのプロンプト
102
  prompt_template = f"""
103
  あなたは会話アプリの演出AIです。以下の条件に基づき、演出プランをJSON形式で生成してください。
104
  生成する内容は必ず健全で、一般的な会話に適したものにしてください。
 
112
  }}
113
  """
114
  try:
115
+ # Groq APIを使用して動的な指示を生成
116
  chat_completion = groq_client.chat.completions.create(
117
  messages=[{"role": "system", "content": "You must generate a response in valid JSON format."},
118
  {"role": "user", "content": prompt_template}],
 
120
  )
121
  response_content = chat_completion.choices[0].message.content
122
  print(f"Groqからの応答: {response_content}") # デバッグ出力
 
123
 
124
+ try:
125
+ # JSONをパース
126
+ params = json.loads(response_content)
127
+
128
+ # 安全のため、initial_dialogue_instructionを簡略化
129
+ if "initial_dialogue_instruction" in params:
130
+ original = params["initial_dialogue_instruction"]
131
+ simplified = f"{scene}の様子について述べる"
132
+ print(f"指示を簡略化: {original} -> {simplified}")
133
+ params["initial_dialogue_instruction"] = simplified
134
+
135
+ # 複雑な構造になっている場合は単純化
136
+ if isinstance(params.get("personality_mod"), dict):
137
+ params["personality_mod"] = f"{scene}での様子を観察している"
138
 
139
+ if isinstance(params.get("tone"), dict):
140
+ params["tone"] = "冷静だが、少し興味を持っている様子"
141
+
142
+ return params
143
+ except json.JSONDecodeError as json_error:
144
+ print(f"JSON解析エラー: {json_error}")
145
+ # JSONの解析に失敗した場合はデフォルトの指示を返す
146
+ default_instruction = {
147
+ "theme": scene,
148
+ "personality_mod": f"{scene}での様子を観察している",
149
+ "tone": "冷静だが、少し興味を持っている様子",
150
+ "initial_dialogue_instruction": f"{scene}の様子について述べる",
151
+ "constraints": ["健全な表現のみ使用する", "シンプルな内容にする"]
152
+ }
153
+ return default_instruction
154
  except Exception as e:
155
  print(f"指示書生成エラー(Groq): {e}")
156
+ # エラーが発生した場合はデフォルトの指示を返す
157
+ default_instruction = {
158
+ "theme": scene,
159
+ "personality_mod": f"{scene}での様子を観察している",
160
+ "tone": "冷静だが、少し興味を持っている様子",
161
+ "initial_dialogue_instruction": f"{scene}の様子について述べる",
162
+ "constraints": ["健全な表現のみ使用する", "シンプルな内容にする"]
163
+ }
164
+ return default_instruction
165
 
166
  def generate_dialogue_with_gemini(history, message, affection, stage_name, scene_params, instruction=None, use_simple_prompt=False):
167
+ # デバッグ情報を追加
168
+ print(f"generate_dialogue_with_gemini呼び出し: instruction={instruction}, use_simple_prompt={use_simple_prompt}")
169
+ print(f"scene_params: {scene_params}")
170
+
171
  history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history])
172
  task_prompt = f"指示: {instruction}" if instruction else f"ユーザー: {message}"
173
 
 
211
  try:
212
  # デバッグ情報を追加
213
  print(f"Gemini API呼び出し開始...")
214
+ print(f"システムプロンプト: {system_prompt[:100]}...(省略)")
215
 
216
+ try:
217
+ generation_config = genai.types.GenerationConfig(max_output_tokens=200, temperature=0.95)
218
+ response = gemini_model.generate_content(
219
+ system_prompt,
220
+ generation_config=generation_config
221
+ )
222
+ print(f"Gemini API呼び出し成功")
223
+ except Exception as api_error:
224
+ print(f"Gemini API呼び出しエラー: {api_error}")
225
+ raise
226
 
227
  print(f"Gemini API呼び出し完了")
228
  print(f"応答候補数: {len(response.candidates) if hasattr(response, 'candidates') and response.candidates else 'なし'}")
 
239
 
240
  # 正常に応答が返されたかどうかを厳密にチェック
241
  if (response.candidates and
242
+ response.candidates[0].finish_reason in {1, 'STOP'}):
 
 
243
 
244
+ try:
245
+ # text属性にアクセスする前に、安全にチェック
246
+ response_text = response.text.strip() if hasattr(response, 'text') else None
247
+ if response_text:
248
+ print(f"正常応答: {response_text}")
249
+ return response_text
250
+ else:
251
+ print("応答にテキストがありません")
252
+ # フォールバック応答を返す
253
+ return "(……何か言おうとしたけど、言葉に詰まった)"
254
+ except Exception as text_error:
255
+ print(f"応答テキスト取得エラー: {text_error}")
256
+ # フォールバック応答を返す
257
+ return "(……何か言おうとしたけど、言葉に詰まった)"
258
  else:
259
  print(f"異常応答検出")
260
  # エラー情報をコンソールに出力(デバッグ用)
 
410
  if new_scene_name and new_scene_name != current_theme:
411
  print(f"シーンチェンジを実行: {current_theme} -> {new_scene_name}")
412
 
413
+ # シーンパラメータを更新(動的な指示を使用)
414
  new_params_base = generate_scene_instruction_with_groq(new_affection, stage_name, new_scene_name, message)
415
  if new_params_base:
 
 
 
 
 
 
 
 
 
 
 
 
 
416
  final_scene_params = {**DEFAULT_SCENE_PARAMS, **new_params_base}
417
+
418
  # シンプルな指示を使用
419
  simple_instruction = f"{new_scene_name}に来た感想を述べる"
420
  print(f"シンプルな指示を使用: {simple_instruction}")
421
 
422
+ try:
423
+ # シーン遷移時は簡潔なプロンプトを使用してGeminiで応答を生成
424
+ bot_message = generate_dialogue_with_gemini(history, message, new_affection, stage_name, final_scene_params, instruction=simple_instruction, use_simple_prompt=True)
425
+ except Exception as scene_error:
426
+ print(f"シーン遷移時の応答生成エラー: {scene_error}")
427
+
428
+ # エラーが発生した場合は、シーンに応じたフォールバック応答を使用
429
+ scene_responses = {
430
+ "aquarium_night": [
431
+ "(水槽の青い光に照らされた魚たちを見つめている)こんな時間に来ると、また違った雰囲���だな。",
432
+ "(暗がりの中で光る魚たちを見て)夜の水族館か…意外と悪くないかも。",
433
+ "(水槽に近づいて)夜になると、昼間とは違う魚が活動してるんだな。"
434
+ ],
435
+ "beach_sunset": [
436
+ "(夕日に照らされた海を見つめて)こんな景色、久しぶりに見たな…",
437
+ "(砂浜に足跡をつけながら)夕暮れの海って、なんか落ち着くな。",
438
+ "(波の音を聞きながら)この時間の浜辺は、人も少なくていいかも。"
439
+ ],
440
+ "festival_night": [
441
+ "(提灯の明かりを見上げて)意外と…悪くない雰囲気だな。",
442
+ "(周囲の賑わいを見回して)こういう場所は、あまり来ないんだけどな…",
443
+ "(屋台の匂いを感じて)なんか…懐かしい感じがするな。"
444
+ ],
445
+ "shrine_day": [
446
+ "(静かな境内を見回して)こういう静かな場所も、たまにはいいかも。",
447
+ "(鳥居を見上げて)なんか、空気が違うな、ここは。",
448
+ "(参道を歩きながら)静かで…落ち着くな。"
449
+ ],
450
+ "cafe_afternoon": [
451
+ "(窓の外を見ながら)こういう時間の過ごし方も、悪くないな。",
452
+ "(コーヒーの香りを感じて)ここの雰囲気、悪くないな。",
453
+ "(店内を見回して)意外と落ち着く場所だな、ここ。"
454
+ ],
455
+ "room_night": [
456
+ "(窓の外の夜景を見て)夜の景色って、なんか落ち着くな。",
457
+ "(部屋の明かりを見つめて)こういう静かな時間も、たまにはいいかも。",
458
+ "(窓際に立ち)夜の静けさって、考え事するのにちょうどいいな。"
459
+ ]
460
+ }
461
+
462
+ import random
463
+ if new_scene_name in scene_responses:
464
+ bot_message = random.choice(scene_responses[new_scene_name])
465
+ else:
466
+ bot_message = f"({new_scene_name}の様子を静かに見回して)ここか…悪くない場所かもな。"
467
  else:
468
  final_scene_params["theme"] = new_scene_name
469
  bot_message = generate_dialogue_with_gemini(history, message, new_affection, stage_name, final_scene_params)
 
486
  <div class="chat-background {theme_name}"></div>
487
  </div>
488
  <style>
489
+ /* 背景画像の設定 */
490
  .chat-background {{
491
  background-image: url({THEME_URLS.get(theme_name, THEME_URLS["default"])}) !important;
492
  }}
493
 
494
  /* 背景コンテナのスタイルを強制的に適用 */
495
+ .background-container, #bg-container-{theme_name} {{
496
  position: fixed !important;
497
  top: 0 !important;
498
  left: 0 !important;
 
500
  height: 100% !important;
501
  z-index: -1000 !important;
502
  pointer-events: none !important;
503
+ overflow: hidden !important;
504
+ }}
505
+
506
+ /* 背景画像のスタイル */
507
+ .chat-background {{
508
+ position: absolute !important;
509
+ top: 0 !important;
510
+ left: 0 !important;
511
+ width: 100% !important;
512
+ height: 100% !important;
513
+ background-size: cover !important;
514
+ background-position: center !important;
515
+ opacity: 0.3 !important;
516
+ filter: blur(1px) !important;
517
+ transition: all 0.5s ease !important;
518
+ }}
519
+
520
+ /* 背景画像の上に半透明のオーバーレイを追加 */
521
+ .background-container::after {{
522
+ content: "" !important;
523
+ position: absolute !important;
524
+ top: 0 !important;
525
+ left: 0 !important;
526
+ width: 100% !important;
527
+ height: 100% !important;
528
+ background: linear-gradient(rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.4)) !important;
529
+ z-index: -999 !important;
530
  }}
531
 
532
  /* Gradioのコンテナを透明に */
533
  .gradio-container, .gradio-container > div {{
534
  background-color: transparent !important;
535
  }}
536
+
537
+ /* チャットボットのスタイル */
538
+ .chatbot {{
539
+ background-color: rgba(255, 255, 255, 0.7) !important;
540
+ border-radius: 12px !important;
541
+ padding: 15px !important;
542
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important;
543
+ margin-bottom: 20px !important;
544
+ }}
545
  </style>
546
  '''
547
 
 
578
  affection_state = gr.State(30)
579
  history_state = gr.State([])
580
 
581
+ # 背景コンテナを先に配置(固定位置で全画面に)- スタイルも含める
582
  background_display = gr.HTML(f'''
583
+ <div class="background-container" id="bg-container-default">
584
  <div class="chat-background {DEFAULT_SCENE_PARAMS["theme"]}"></div>
585
  </div>
586
+ <style>
587
+ /* 背景画像の設定 */
588
+ .chat-background {{
589
+ background-image: url({THEME_URLS.get(DEFAULT_SCENE_PARAMS["theme"], THEME_URLS["default"])}) !important;
590
+ }}
591
+
592
+ /* 背景コンテナのスタイルを強制的に適用 */
593
+ .background-container, #bg-container-default {{
594
+ position: fixed !important;
595
+ top: 0 !important;
596
+ left: 0 !important;
597
+ width: 100% !important;
598
+ height: 100% !important;
599
+ z-index: -1000 !important;
600
+ pointer-events: none !important;
601
+ overflow: hidden !important;
602
+ }}
603
+
604
+ /* 背景画像のスタイル */
605
+ .chat-background {{
606
+ position: absolute !important;
607
+ top: 0 !important;
608
+ left: 0 !important;
609
+ width: 100% !important;
610
+ height: 100% !important;
611
+ background-size: cover !important;
612
+ background-position: center !important;
613
+ opacity: 0.3 !important;
614
+ filter: blur(1px) !important;
615
+ transition: all 0.5s ease !important;
616
+ }}
617
+
618
+ /* 背景画像の上に半透明のオーバーレイを追加 */
619
+ .background-container::after {{
620
+ content: "" !important;
621
+ position: absolute !important;
622
+ top: 0 !important;
623
+ left: 0 !important;
624
+ width: 100% !important;
625
+ height: 100% !important;
626
+ background: linear-gradient(rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.4)) !important;
627
+ z-index: -999 !important;
628
+ }}
629
+ </style>
630
  ''', elem_id="background_container")
631
 
632
  # ヘッダー部分(背景と分離)