sirochild commited on
Commit
6027380
·
verified ·
1 Parent(s): 5dc10e2

Upload 5 files

Browse files
Files changed (2) hide show
  1. app.py +87 -15
  2. style.css +25 -0
app.py CHANGED
@@ -21,9 +21,21 @@ def create_limiter_state():
21
  return {"timestamps": [], "is_blocked": False}
22
 
23
  def check_limiter(limiter_state):
24
- if limiter_state["is_blocked"]: return False
 
 
 
 
 
 
 
25
  now = time.time()
26
- limiter_state["timestamps"] = [t for t in limiter_state["timestamps"] if now - t < RATE_LIMIT_IN_SECONDS]
 
 
 
 
 
27
  if len(limiter_state["timestamps"]) >= RATE_LIMIT_MAX_REQUESTS:
28
  logger.error("レートリミット超過! API呼び出しをブロックします。")
29
  limiter_state["is_blocked"] = True
@@ -159,25 +171,73 @@ def detect_scene_change(history, message):
159
  return None # この関数はrespond内で直接ロジックを記述
160
 
161
  def generate_dialogue(history, message, affection, stage_name, scene_params, instruction=None):
162
- # (中身は変更なし)
163
- history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history])
164
- user_prompt = f'# 現在の状況\n- 現在地: {scene_params.get("theme", "default")}\n- 好感度: {affection} ({stage_name})\n\n# 会話履歴\n{history_text}\n---\n# 指示\n{f"【特別指示】{instruction}" if instruction else f"ユーザーの発言「{message}」に応答してください。"}\n\n麻理の応答:'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  return call_llm(SYSTEM_PROMPT_MARI, user_prompt)
166
 
167
  def get_relationship_stage(affection):
168
- # (中身は変更なし)
169
- if affection < 40: return "ステージ1:警戒"; # ...
170
- return "ステージ4:親密"
 
 
 
 
 
 
 
 
 
 
171
 
172
  def update_affection(message, affection):
173
- # (中身は変更なし)
 
 
174
  analyzer = get_sentiment_analyzer()
175
- if not analyzer: return affection
 
 
176
  try:
 
 
 
177
  result = analyzer(message)[0]
178
- if result['label'] == 'positive': return min(100, affection + 3)
179
- if result['label'] == 'negative': return max(0, affection - 3)
180
- except Exception: pass
 
 
 
 
181
  return affection
182
 
183
  # --- 6. Gradio応答関数 (v5構文に完全対応) ---
@@ -213,6 +273,10 @@ def respond(message, chat_history, affection, scene_params, limiter_state):
213
 
214
  new_affection = update_affection(message, affection)
215
  stage_name = get_relationship_stage(new_affection)
 
 
 
 
216
  final_scene_params = scene_params.copy()
217
 
218
  bot_message = ""
@@ -256,13 +320,21 @@ def respond(message, chat_history, affection, scene_params, limiter_state):
256
  try:
257
  parsed = json.loads(new_scene_name_json)
258
  if isinstance(parsed, dict):
259
- new_scene_name = parsed.get("scene")
 
 
 
 
 
260
  else:
261
  logger.warning(f"想定外のJSON形式が返されました: {parsed}")
262
  except Exception as e:
263
  logger.error(f"JSONパースに失敗しました: {e}\n元の出力: {new_scene_name_json}")
264
 
265
- if new_scene_name and new_scene_name != "none" and new_scene_name != final_scene_params.get("theme"):
 
 
 
266
  if not check_limiter(limiter_state):
267
  bot_message = "(…少し考える時間がほしい)"
268
  else:
 
21
  return {"timestamps": [], "is_blocked": False}
22
 
23
  def check_limiter(limiter_state):
24
+ # limiter_stateが辞書であることを確認
25
+ if not isinstance(limiter_state, dict):
26
+ logger.error(f"limiter_stateが辞書ではありません: {type(limiter_state)}")
27
+ return False
28
+
29
+ if limiter_state.get("is_blocked", False):
30
+ return False
31
+
32
  now = time.time()
33
+ timestamps = limiter_state.get("timestamps", [])
34
+ if not isinstance(timestamps, list):
35
+ timestamps = []
36
+ limiter_state["timestamps"] = timestamps
37
+
38
+ limiter_state["timestamps"] = [t for t in timestamps if now - t < RATE_LIMIT_IN_SECONDS]
39
  if len(limiter_state["timestamps"]) >= RATE_LIMIT_MAX_REQUESTS:
40
  logger.error("レートリミット超過! API呼び出しをブロックします。")
41
  limiter_state["is_blocked"] = True
 
171
  return None # この関数はrespond内で直接ロジックを記述
172
 
173
  def generate_dialogue(history, message, affection, stage_name, scene_params, instruction=None):
174
+ if not isinstance(history, list):
175
+ history = []
176
+ if not isinstance(scene_params, dict):
177
+ scene_params = {"theme": "default"}
178
+ if not isinstance(message, str):
179
+ message = ""
180
+
181
+ # 履歴を安全に処理
182
+ safe_history = []
183
+ for item in history:
184
+ if isinstance(item, (list, tuple)) and len(item) >= 2:
185
+ user_msg = str(item[0]) if item[0] is not None else ""
186
+ bot_msg = str(item[1]) if item[1] is not None else ""
187
+ safe_history.append((user_msg, bot_msg))
188
+
189
+ history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in safe_history])
190
+
191
+ current_theme = scene_params.get("theme", "default")
192
+ user_prompt = f'''# 現在の状況
193
+ - 現在地: {current_theme}
194
+ - 好感度: {affection} ({stage_name})
195
+
196
+ # 会話履歴
197
+ {history_text}
198
+ ---
199
+ # 指示
200
+ {f"【特別指示】{instruction}" if instruction else f"ユーザーの発言「{message}」に応答してください。"}
201
+
202
+ 麻理の応答:'''
203
+
204
  return call_llm(SYSTEM_PROMPT_MARI, user_prompt)
205
 
206
  def get_relationship_stage(affection):
207
+ if not isinstance(affection, (int, float)):
208
+ affection = 30 # デフォルト値
209
+
210
+ if affection < 20:
211
+ return "ステージ1:敵対"
212
+ elif affection < 40:
213
+ return "ステージ2:警戒"
214
+ elif affection < 60:
215
+ return "ステージ3:中立"
216
+ elif affection < 80:
217
+ return "ステージ4:好意"
218
+ else:
219
+ return "ステージ5:親密"
220
 
221
  def update_affection(message, affection):
222
+ if not isinstance(affection, (int, float)):
223
+ affection = 30 # デフォルト値
224
+
225
  analyzer = get_sentiment_analyzer()
226
+ if not analyzer:
227
+ return affection
228
+
229
  try:
230
+ if not isinstance(message, str) or len(message.strip()) == 0:
231
+ return affection
232
+
233
  result = analyzer(message)[0]
234
+ if result.get('label') == 'positive':
235
+ return min(100, affection + 3)
236
+ elif result.get('label') == 'negative':
237
+ return max(0, affection - 3)
238
+ except Exception as e:
239
+ logger.error(f"感情分析エラー: {e}")
240
+
241
  return affection
242
 
243
  # --- 6. Gradio応答関数 (v5構文に完全対応) ---
 
273
 
274
  new_affection = update_affection(message, affection)
275
  stage_name = get_relationship_stage(new_affection)
276
+
277
+ # scene_paramsが辞書であることを確認
278
+ if not isinstance(scene_params, dict):
279
+ scene_params = {"theme": "default"}
280
  final_scene_params = scene_params.copy()
281
 
282
  bot_message = ""
 
320
  try:
321
  parsed = json.loads(new_scene_name_json)
322
  if isinstance(parsed, dict):
323
+ scene_value = parsed.get("scene")
324
+ # scene_valueが文字列であることを確認
325
+ if isinstance(scene_value, str):
326
+ new_scene_name = scene_value
327
+ else:
328
+ logger.warning(f"scene値が文字列ではありません: {scene_value} (型: {type(scene_value)})")
329
  else:
330
  logger.warning(f"想定外のJSON形式が返されました: {parsed}")
331
  except Exception as e:
332
  logger.error(f"JSONパースに失敗しました: {e}\n元の出力: {new_scene_name_json}")
333
 
334
+ if (new_scene_name and
335
+ isinstance(new_scene_name, str) and
336
+ new_scene_name != "none" and
337
+ new_scene_name != final_scene_params.get("theme")):
338
  if not check_limiter(limiter_state):
339
  bot_message = "(…少し考える時間がほしい)"
340
  else:
style.css CHANGED
@@ -10,6 +10,31 @@ body {
10
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
11
  }
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  /* --- Layout --- */
14
  .gradio-container {
15
  max-width: 1000px !important;
 
10
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
11
  }
12
 
13
+ /* Gradio内部のフォント参照を上書き */
14
+ @font-face {
15
+ font-family: 'ui-sans-serif';
16
+ src: local('system-ui'), local('-apple-system'), local('BlinkMacSystemFont');
17
+ font-weight: normal;
18
+ }
19
+
20
+ @font-face {
21
+ font-family: 'ui-sans-serif';
22
+ src: local('system-ui'), local('-apple-system'), local('BlinkMacSystemFont');
23
+ font-weight: bold;
24
+ }
25
+
26
+ @font-face {
27
+ font-family: 'system-ui';
28
+ src: local('system-ui'), local('-apple-system'), local('BlinkMacSystemFont');
29
+ font-weight: normal;
30
+ }
31
+
32
+ @font-face {
33
+ font-family: 'system-ui';
34
+ src: local('system-ui'), local('-apple-system'), local('BlinkMacSystemFont');
35
+ font-weight: bold;
36
+ }
37
+
38
  /* --- Layout --- */
39
  .gradio-container {
40
  max-width: 1000px !important;