sirochild commited on
Commit
027b5ee
·
verified ·
1 Parent(s): acd8b47

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +25 -41
  2. style.css +26 -26
app.py CHANGED
@@ -8,23 +8,16 @@ from transformers import pipeline
8
  import re
9
 
10
  # --- 1. 初期設定とAPIクライアントの初期化 ---
11
- # Hugging Face SpacesのSecretsからAPIキーを読み込む
12
  load_dotenv()
13
-
14
- # Geminiクライアント
15
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
16
- if not GEMINI_API_KEY:
17
- raise ValueError("SecretsにGEMINI_API_KEYが見つかりません。")
 
 
18
  genai.configure(api_key=GEMINI_API_KEY)
19
  gemini_model = genai.GenerativeModel('gemini-1.5-flash-latest')
20
-
21
- # Groqクライアント
22
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
23
- if not GROQ_API_KEY:
24
- raise ValueError("SecretsにGROQ_API_KEYが見つかりません。")
25
  groq_client = Groq(api_key=GROQ_API_KEY)
26
 
27
- # 感情分析モデル
28
  print("日本語感情分析モデルをロード中...")
29
  try:
30
  sentiment_analyzer = pipeline("sentiment-analysis", model="koheiduck/bert-japanese-finetuned-sentiment")
@@ -34,14 +27,11 @@ except Exception as e:
34
  print(f"モデルのロードエラー: {e}")
35
 
36
  DEFAULT_SCENE_PARAMS = {
37
- "theme": "default",
38
- "personality_mod": "ストレートな感情表現をするが、少しぶっきらぼう。優しさと冷たさが混在している。",
39
- "tone": "普通のトーン。",
40
- "constraints": ["キャラクター設定から逸脱しない"]
41
  }
42
 
43
  # --- 2. LLMによる役割分担システム ---
44
-
45
  def detect_scene_change(history, message):
46
  history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history[-4:]])
47
  prompt = f"""
@@ -49,11 +39,11 @@ def detect_scene_change(history, message):
49
  # タスク
50
  直近の会話履歴と最後のユーザー発言を分析し、**会話の結果として登場人物がどこか特定の場所へ行くことに合意したか**を判断してください。
51
  # 判断基準
52
- 1. 会話の中で具体的な場所(例:水族館、カフェ、神社)が提案されているか?
53
  2. 最後のユーザー発言が、その提案に対する明確な同意(例:「行こう」「いいね」「そうしよう」など)を示しているか?
54
  # 出力形式
55
- - 合意が成立した場合:その場所の英語キーワード(例: "aquarium_night", "cafe_afternoon")を一つだけ出力してください。
56
- - 合意に至らなかった場合:「none」とだけ出力してください。
57
  ---
58
  # 分析対象の会話
59
  {history_text}
@@ -73,30 +63,22 @@ def detect_scene_change(history, message):
73
  return None
74
 
75
  def generate_scene_instruction_with_groq(affection, stage_name, scene, previous_topic):
76
- print("Groq/Llama3に「指示書」生成をリクエストします...")
77
  prompt_template = f"""
78
  あなたは会話アプリの演出AIです。以下の条件に基づき、演出プランをJSON形式で生成してください。
79
- 【条件】
80
- 好感度:{affection}
81
- 関係段階:「{stage_name}」
82
- シーン名:「{scene}」
83
- 直前の話題:「{previous_topic}」
84
- 【指示】
85
- - `initial_dialogue_instruction`には、麻理が言うべき最初のセリフの内容や感情の指示を日本語で記述してください。実際のセリフは絶対に生成しないでください。
86
- - `personality_mod`と`tone`は、シーンと現在の関係性を反映した簡潔なものにしてください。
87
- - 必ず、以下のJSON形式のみを出力してください。説明文は不要です。
88
  {{
89
  "theme": "{scene}",
90
- "personality_mod": "(シーンと関係段階に応じた性格設定)",
91
- "tone": "(シーンと好感度に応じた口調や感情トーン)",
92
- "initial_dialogue_instruction": "(例:少し戸惑いながらも、提案された場所への期待感を滲ませる)",
93
  "constraints": ["(出力時の制約1)", "(制約2)"]
94
  }}
95
  """
96
  try:
97
  chat_completion = groq_client.chat.completions.create(
98
- messages=[{"role": "user", "content": prompt_template}],
99
- model="llama3-8b-8192", temperature=0.7, response_format={"type": "json_object"},
 
100
  )
101
  params = json.loads(chat_completion.choices[0].message.content)
102
  print("生成された指示書JSON:", params)
@@ -165,27 +147,29 @@ def respond(message, chat_history, affection, history, scene_params):
165
  new_history = history + [(message, bot_message)]
166
  chat_history.append((message, bot_message))
167
  theme_name = final_scene_params.get("theme", "default")
 
 
168
  js_script = f"""
169
  <script>
170
  setTimeout(() => {{
171
  const body = document.body;
172
- const themes = ["theme-default", "theme-room_night", "theme-beach_sunset", "theme-festival_night", "theme-shrine_day", "theme-cafe_afternoon", "theme-aquarium_night"];
173
- body.classList.remove(...themes);
174
- body.classList.add("theme-{theme_name}");
175
- }}, 100);
176
  </script>
177
  """
 
178
  return "", chat_history, new_affection, stage_name, new_affection, new_history, final_scene_params, js_script
179
 
180
- with gr.Blocks(css="style.css", theme=None) as demo:
181
  affection_state = gr.State(30)
182
  history_state = gr.State([])
183
  scene_state = gr.State(DEFAULT_SCENE_PARAMS)
184
  gr.Markdown("# 麻理チャット")
185
  with gr.Row():
186
  with gr.Column(scale=2):
187
- chatbot = gr.Chatbot(label="麻理との会話", height=500)
188
- msg_input = gr.Textbox(label="あなたのメッセージ", placeholder="「水族館はどう?」と聞いた後、「いいね、行こう!」のように返してみてください")
189
  with gr.Column(scale=1):
190
  stage_display = gr.Textbox(label="現在の関係ステージ", interactive=False)
191
  affection_gauge = gr.Slider(minimum=0, maximum=100, label="麻理の好感度", interactive=False)
 
8
  import re
9
 
10
  # --- 1. 初期設定とAPIクライアントの初期化 ---
 
11
  load_dotenv()
 
 
12
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
13
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
14
+ if not GEMINI_API_KEY or not GROQ_API_KEY:
15
+ raise ValueError("GEMINI_API_KEY と GROQ_API_KEY をSecretsに設定してください。")
16
+
17
  genai.configure(api_key=GEMINI_API_KEY)
18
  gemini_model = genai.GenerativeModel('gemini-1.5-flash-latest')
 
 
 
 
 
19
  groq_client = Groq(api_key=GROQ_API_KEY)
20
 
 
21
  print("日本語感情分析モデルをロード中...")
22
  try:
23
  sentiment_analyzer = pipeline("sentiment-analysis", model="koheiduck/bert-japanese-finetuned-sentiment")
 
27
  print(f"モデルのロードエラー: {e}")
28
 
29
  DEFAULT_SCENE_PARAMS = {
30
+ "theme": "default", "personality_mod": "ストレートだが根は優しい",
31
+ "tone": "普通のトーン", "constraints": ["キャラクター設定から逸脱しない"]
 
 
32
  }
33
 
34
  # --- 2. LLMによる役割分担システム ---
 
35
  def detect_scene_change(history, message):
36
  history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history[-4:]])
37
  prompt = f"""
 
39
  # タスク
40
  直近の会話履歴と最後のユーザー発言を分析し、**会話の結果として登場人物がどこか特定の場所へ行くことに合意したか**を判断してください。
41
  # 判断基準
42
+ 1. 会話の中で具体的な場所が提案されているか?
43
  2. 最後のユーザー発言が、その提案に対する明確な同意(例:「行こう」「いいね」「そうしよう」など)を示しているか?
44
  # 出力形式
45
+ - 合意が成立した場合:その場所の英語キーワード(例: "aquarium_night")を一つだけ出力。
46
+ - 合意に至らなかった場合:「none」とだけ出力。
47
  ---
48
  # 分析対象の会話
49
  {history_text}
 
63
  return None
64
 
65
  def generate_scene_instruction_with_groq(affection, stage_name, scene, previous_topic):
66
+ print(f"Groqに指示書生成をリクエスト (シーン: {scene})")
67
  prompt_template = f"""
68
  あなたは会話アプリの演出AIです。以下の条件に基づき、演出プランをJSON形式で生成してください。
 
 
 
 
 
 
 
 
 
69
  {{
70
  "theme": "{scene}",
71
+ "personality_mod": "(シーンと関係段階「{stage_name}」に応じた性格設定)",
72
+ "tone": "(シーンと好感度「{affection}」に応じた口調や感情トーン)",
73
+ "initial_dialogue_instruction": "(「{previous_topic}」という話題から、シーン遷移直後の麻理が言うべきセリフの指示を日本語で記述)",
74
  "constraints": ["(出力時の制約1)", "(制約2)"]
75
  }}
76
  """
77
  try:
78
  chat_completion = groq_client.chat.completions.create(
79
+ messages=[{"role": "system", "content": "You must generate a response in valid JSON format."},
80
+ {"role": "user", "content": prompt_template}],
81
+ model="llama3-8b-8192", temperature=0.8, response_format={"type": "json_object"},
82
  )
83
  params = json.loads(chat_completion.choices[0].message.content)
84
  print("生成された指示書JSON:", params)
 
147
  new_history = history + [(message, bot_message)]
148
  chat_history.append((message, bot_message))
149
  theme_name = final_scene_params.get("theme", "default")
150
+
151
+ # JavaScriptを、data-theme属性を書き換えるように変更
152
  js_script = f"""
153
  <script>
154
  setTimeout(() => {{
155
  const body = document.body;
156
+ console.log("Applying data-theme:", "{theme_name}");
157
+ body.setAttribute('data-theme', 'theme-{theme_name}');
158
+ }}, 50);
 
159
  </script>
160
  """
161
+
162
  return "", chat_history, new_affection, stage_name, new_affection, new_history, final_scene_params, js_script
163
 
164
+ with gr.Blocks(css="style.css", theme=gr.themes.Soft(primary_hue="rose", secondary_hue="pink")) as demo:
165
  affection_state = gr.State(30)
166
  history_state = gr.State([])
167
  scene_state = gr.State(DEFAULT_SCENE_PARAMS)
168
  gr.Markdown("# 麻理チャット")
169
  with gr.Row():
170
  with gr.Column(scale=2):
171
+ chatbot = gr.Chatbot(label="麻理との会話", height=500, bubble_full_width=False)
172
+ msg_input = gr.Textbox(label="あなたのメッセージ", placeholder="「水族館はどう?」と聞いた後、「いいね、行こう!」のように返してみてください", scale=5)
173
  with gr.Column(scale=1):
174
  stage_display = gr.Textbox(label="現在の関係ステージ", interactive=False)
175
  affection_gauge = gr.Slider(minimum=0, maximum=100, label="麻理の好感度", interactive=False)
style.css CHANGED
@@ -1,41 +1,41 @@
1
- /* GradioのUI全体をラップするコンテナを指定 */
2
- body .gradio-container {
3
- transition: background-image 1s ease-in-out;
4
- background-image: var(--background-image, url('https://i.ibb.co/XzP6K2Y/room-day.png')) !important;
5
- background-size: cover !important;
6
- background-position: center !important;
7
- }
8
-
9
- /* 各テーマの定義 */
10
- body.theme-default .gradio-container {
11
  --background-image: url('https://i.ibb.co/XzP6K2Y/room-day.png');
12
  }
13
- body.theme-room_night .gradio-container {
14
  --background-image: url('https://i.ibb.co/d5m821p/room-night.png');
15
  }
16
- body.theme-beach_sunset .gradio-container {
17
  --background-image: url('https://i.ibb.co/Q9r56s4/beach-sunset.png');
18
  }
19
- body.theme-festival_night .gradio-container {
20
  --background-image: url('https://i.ibb.co/3zdJ6Bw/festival-night.png');
21
  }
22
- body.theme-shrine_day .gradio-container {
23
  --background-image: url('https://i.ibb.co/L51Jd3x/shrine-day.png');
24
  }
25
- body.theme-cafe_afternoon .gradio-container {
26
  --background-image: url('https://i.ibb.co/yQxG4vs/cafe-afternoon.png');
27
  }
28
- body.theme-aquarium_night .gradio-container {
29
- --background-image: url('https://i.ibb.co/dK5r5rc/aquarium-night.png');
30
  }
31
 
32
- /* チャットウィンドウなどのスタイル */
33
- .gradio-container .gr-chatbot .wrap {
34
- background-color: rgba(255, 255, 255, 0.7) !important;
35
- backdrop-filter: blur(5px);
36
- }
37
- .gradio-container .gr-textbox,
38
- .gradio-container .gr-slider {
39
- background-color: rgba(255, 255, 255, 0.85) !important;
40
- border-radius: 10px !important;
41
  }
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* bodyタグの属性を直接ターゲットにする */
2
+ body[data-theme="theme-default"] {
 
 
 
 
 
 
 
 
3
  --background-image: url('https://i.ibb.co/XzP6K2Y/room-day.png');
4
  }
5
+ body[data-theme="theme-room_night"] {
6
  --background-image: url('https://i.ibb.co/d5m821p/room-night.png');
7
  }
8
+ body[data-theme="theme-beach_sunset"] {
9
  --background-image: url('https://i.ibb.co/Q9r56s4/beach-sunset.png');
10
  }
11
+ body[data-theme="theme-festival_night"] {
12
  --background-image: url('https://i.ibb.co/3zdJ6Bw/festival-night.png');
13
  }
14
+ body[data-theme="theme-shrine_day"] {
15
  --background-image: url('https://i.ibb.co/L51Jd3x/shrine-day.png');
16
  }
17
+ body[data-theme="theme-cafe_afternoon"] {
18
  --background-image: url('https://i.ibb.co/yQxG4vs/cafe-afternoon.png');
19
  }
20
+ body[data-theme="theme-aquarium_night"] {
21
+ --background-image: url('https://i.ibb.co/dK5r5rc/aquarium-night.png');
22
  }
23
 
24
+ /* どのテーマでも背景画像を適用するための共通ルール */
25
+ body {
26
+ transition: background-image 1s ease-in-out;
27
+ background-image: var(--background-image) !important;
28
+ background-size: cover !important;
29
+ background-position: center !important;
 
 
 
30
  }
31
+
32
+ /* チャットウィンドウなどのスタイル */
33
+ .gradio-chatbot .chatbot {
34
+ background-color: rgba(255, 255, 255, 0.75) !important;
35
+ backdrop-filter: blur(8px);
36
+ border: 1px solid rgba(255, 255, 255, 0.2);
37
+ }
38
+ .gradio-textbox, .gradio-slider {
39
+ background-color: rgba(255, 255, 255, 0.85) !important;
40
+ border-radius: 10px !important;
41
+ }