cereeenn120 commited on
Commit
832430f
·
1 Parent(s): 556ebc5

Add logger with verbosity

Browse files
app.py CHANGED
@@ -39,7 +39,7 @@ LANGUAGES = ["English", "French", "German", "Italian", "Russian", "Turkish", "Ch
39
 
40
  DEFAULT_MODEL = MODELS[14][1]
41
  DEFAULT_LANGUAGE = "English"
42
- TRANSLATION_MODEL = "openai/gpt-4o-mini"
43
 
44
  #MODEL = "deepseek-ai/DeepSeek-V3" # or e.g. "openai/gpt-4o-mini"
45
  #MODEL = "Qwen/Qwen3-235B-A22B-Instruct-2507"
@@ -145,6 +145,7 @@ chatbot = gr.ChatInterface(
145
  # ── Event handlers ─────────────────────────────────────────────────────────────
146
 
147
  def on_character_change(new_char_id: str):
 
148
  try:
149
  char = load_character(new_char_id)
150
  except Exception as e:
@@ -154,6 +155,7 @@ def on_character_change(new_char_id: str):
154
  role = char.get("identity", {}).get("role", "")
155
  greet = char.get("greeting", "Hello. What do you want?")
156
  avatar = get_avatar_path(new_char_id)
 
157
  return (
158
  new_char_id,
159
  f"## {name}",
@@ -173,25 +175,32 @@ def apply_persona_fn(name, age, gender, personality, background):
173
  "personality": personality, "background": background,
174
  })
175
  if is_persona_empty(persona):
 
176
  return persona, "*No persona set.*"
 
177
  return persona, f"*✓ Active: {persona_summary(persona)}*"
178
 
179
 
180
  def clear_persona_fn():
 
181
  # Returns: persona_state, 5 form fields, status
182
  return get_empty_persona(), "", "", "", "", "", "*Persona cleared.*"
183
 
184
 
185
  def get_suggestions_fn(char_id: str, model_id: str):
 
186
  if not HF_TOKEN:
 
187
  return [gr.update(visible=False)] + [gr.update(visible=False)] * 4 + [""] * 4
188
  recent = load_recent_messages(char_id, max_messages=6)
189
  if not recent:
 
190
  return [gr.update(visible=False)] + [gr.update(visible=False)] * 4 + [""] * 4
191
  try:
192
  char = load_character(char_id)
193
  client = InferenceClient(token=HF_TOKEN)
194
  suggestions = generate_suggestions(char, recent, client, model_id)
 
195
  except Exception as e:
196
  logger.error(f"[App] Suggestion error: {e}")
197
  return [gr.update(visible=False)] + [gr.update(visible=False)] * 4 + [""] * 4
@@ -204,11 +213,13 @@ def get_suggestions_fn(char_id: str, model_id: str):
204
 
205
  def save_conversation_fn(char_id: str, persona: dict):
206
  """Write a save file and return its path for Gradio to serve as download."""
 
207
  try:
208
  path = export_save(char_id, persona)
 
209
  return gr.update(value=path, visible=True), "*✓ Save file ready — click to download.*"
210
  except Exception as e:
211
- logger.error(f"[App] Save failed: {e}")
212
  return gr.update(visible=False), f"*❌ Save failed: {e}*"
213
 
214
 
@@ -226,15 +237,18 @@ def load_conversation_fn(file_path, current_char_id: str):
226
  no_op = [gr.update()] * 12 + [""]
227
 
228
  if not file_path:
 
229
  return no_op
230
 
 
231
  try:
232
  data = import_save(file_path)
233
  restore_save(data)
234
  except (ValueError, KeyError) as e:
 
235
  return no_op[:-1] + [f"*❌ {e}*"]
236
  except Exception as e:
237
- logger.error(f"[App] Load error: {e}")
238
  return no_op[:-1] + [f"*❌ Unexpected error: {type(e).__name__}*"]
239
 
240
  char_id = data["character_id"]
 
39
 
40
  DEFAULT_MODEL = MODELS[14][1]
41
  DEFAULT_LANGUAGE = "English"
42
+ TRANSLATION_MODEL = "google/gemma-3-27b-it"
43
 
44
  #MODEL = "deepseek-ai/DeepSeek-V3" # or e.g. "openai/gpt-4o-mini"
45
  #MODEL = "Qwen/Qwen3-235B-A22B-Instruct-2507"
 
145
  # ── Event handlers ─────────────────────────────────────────────────────────────
146
 
147
  def on_character_change(new_char_id: str):
148
+ logger.info(f"[App] Character change requested → '{new_char_id}'")
149
  try:
150
  char = load_character(new_char_id)
151
  except Exception as e:
 
155
  role = char.get("identity", {}).get("role", "")
156
  greet = char.get("greeting", "Hello. What do you want?")
157
  avatar = get_avatar_path(new_char_id)
158
+ logger.info(f"[App] Character loaded: name='{name}', role='{role}', avatar={'found' if avatar else 'missing'}")
159
  return (
160
  new_char_id,
161
  f"## {name}",
 
175
  "personality": personality, "background": background,
176
  })
177
  if is_persona_empty(persona):
178
+ logger.info("[App] Persona applied but all fields empty — no persona active.")
179
  return persona, "*No persona set.*"
180
+ logger.info(f"[App] Persona applied: {persona_summary(persona)}")
181
  return persona, f"*✓ Active: {persona_summary(persona)}*"
182
 
183
 
184
  def clear_persona_fn():
185
+ logger.info("[App] Persona cleared.")
186
  # Returns: persona_state, 5 form fields, status
187
  return get_empty_persona(), "", "", "", "", "", "*Persona cleared.*"
188
 
189
 
190
  def get_suggestions_fn(char_id: str, model_id: str):
191
+ logger.info(f"[App] Suggestion request: char='{char_id}', model='{model_id}'")
192
  if not HF_TOKEN:
193
+ logger.warning("[App] Suggestions skipped — HF_TOKEN not set.")
194
  return [gr.update(visible=False)] + [gr.update(visible=False)] * 4 + [""] * 4
195
  recent = load_recent_messages(char_id, max_messages=6)
196
  if not recent:
197
+ logger.info("[App] Suggestions skipped — no recent messages.")
198
  return [gr.update(visible=False)] + [gr.update(visible=False)] * 4 + [""] * 4
199
  try:
200
  char = load_character(char_id)
201
  client = InferenceClient(token=HF_TOKEN)
202
  suggestions = generate_suggestions(char, recent, client, model_id)
203
+ logger.info(f"[App] Suggestions generated: {[s['tone'] for s in suggestions]}")
204
  except Exception as e:
205
  logger.error(f"[App] Suggestion error: {e}")
206
  return [gr.update(visible=False)] + [gr.update(visible=False)] * 4 + [""] * 4
 
213
 
214
  def save_conversation_fn(char_id: str, persona: dict):
215
  """Write a save file and return its path for Gradio to serve as download."""
216
+ logger.info(f"[App] Save requested for char='{char_id}'")
217
  try:
218
  path = export_save(char_id, persona)
219
+ logger.info(f"[App] Save successful → {path}")
220
  return gr.update(value=path, visible=True), "*✓ Save file ready — click to download.*"
221
  except Exception as e:
222
+ logger.error(f"[App] Save failed: {e}", exc_info=True)
223
  return gr.update(visible=False), f"*❌ Save failed: {e}*"
224
 
225
 
 
237
  no_op = [gr.update()] * 12 + [""]
238
 
239
  if not file_path:
240
+ logger.debug("[App] Load called with no file path — skipping.")
241
  return no_op
242
 
243
+ logger.info(f"[App] Load requested: {file_path}")
244
  try:
245
  data = import_save(file_path)
246
  restore_save(data)
247
  except (ValueError, KeyError) as e:
248
+ logger.warning(f"[App] Load validation error: {e}")
249
  return no_op[:-1] + [f"*❌ {e}*"]
250
  except Exception as e:
251
+ logger.error(f"[App] Load error: {e}", exc_info=True)
252
  return no_op[:-1] + [f"*❌ Unexpected error: {type(e).__name__}*"]
253
 
254
  char_id = data["character_id"]
engine/chat.py CHANGED
@@ -62,16 +62,19 @@ def generate_response(
62
  """
63
 
64
 
 
 
65
  # ── Load character ─────────────────────────────────────────────────────────
66
 
67
  try:
68
  character = load_character(character_id)
 
69
  except FileNotFoundError:
70
  logger.error(f"[Chat] Character not found: {character_id}")
71
  yield f"*(Character '{character_id}' could not be loaded.)*"
72
  return
73
  except Exception as e:
74
- logger.error(f"[Chat] Unexpected error loading character: {e}")
75
  yield "*(Something went wrong loading the character. Check the logs.)*"
76
  return
77
 
@@ -79,13 +82,21 @@ def generate_response(
79
  recent_messages = load_recent_messages(character_id)
80
  summary = load_summary(character_id)
81
  long_term_memory = load_long_term_memory(character_id)
 
 
 
 
 
82
 
83
  # ── Translation mode? ──────────────────────────────────────────────────────
84
  use_translation = language.strip().lower() != "english" and bool(translation_model)
 
85
 
86
  if use_translation:
87
  # Translate user message → English before sending to main model
 
88
  en_message = translate_to_english(message, language, client, translation_model)
 
89
  prompt_language = "English" # main model always responds in English
90
  else:
91
  en_message = message
@@ -101,8 +112,13 @@ def generate_response(
101
  current_message=en_message,
102
  language=prompt_language,
103
  )
 
 
 
 
104
 
105
  # ── LLM call ──────────────────────────────────────────────────────────────
 
106
 
107
  raw_response = ""
108
 
@@ -137,25 +153,31 @@ def generate_response(
137
  yield raw_response
138
 
139
  except Exception as e:
140
- logger.error(f"[Chat] LLM error: {e}")
141
  error_note = "\n\n*(The model did not respond. Please try again.)*"
142
  yield (raw_response + error_note) if raw_response else error_note.strip()
143
  return
144
 
 
 
145
  # ── Post-stream ────────────────────────────────────────────────────────────
146
  # Memory always stores English (so main model gets consistent context)
147
  try:
148
  append_message(character_id, "user", en_message)
149
  append_message(character_id, "assistant", raw_response)
 
150
  except Exception as e:
151
- logger.error(f"[Chat] Memory save failed: {e}")
152
 
153
  # ── Translate response → user's language, format, yield ───────────────────
154
  if use_translation:
 
155
  final_text = translate_from_english(raw_response, language, client, translation_model)
156
  else:
157
  final_text = raw_response
158
 
159
  segments = parse_response(final_text)
 
160
  formatted = segments_to_markdown(segments)
 
161
  yield formatted
 
62
  """
63
 
64
 
65
+ logger.info(f"[Chat] ▶ character='{character_id}' | language='{language}' | model='{model}'")
66
+
67
  # ── Load character ─────────────────────────────────────────────────────────
68
 
69
  try:
70
  character = load_character(character_id)
71
+ logger.debug(f"[Chat] Character loaded: '{character.get('name', character_id)}'")
72
  except FileNotFoundError:
73
  logger.error(f"[Chat] Character not found: {character_id}")
74
  yield f"*(Character '{character_id}' could not be loaded.)*"
75
  return
76
  except Exception as e:
77
+ logger.error(f"[Chat] Unexpected error loading character: {e}", exc_info=True)
78
  yield "*(Something went wrong loading the character. Check the logs.)*"
79
  return
80
 
 
82
  recent_messages = load_recent_messages(character_id)
83
  summary = load_summary(character_id)
84
  long_term_memory = load_long_term_memory(character_id)
85
+ logger.debug(
86
+ f"[Chat] Memory: {len(recent_messages)} recent messages | "
87
+ f"summary={'yes' if summary else 'no'} | "
88
+ f"long_term={len(long_term_memory)} entries"
89
+ )
90
 
91
  # ── Translation mode? ──────────────────────────────────────────────────────
92
  use_translation = language.strip().lower() != "english" and bool(translation_model)
93
+ logger.info(f"[Chat] Translation mode: {'ON → ' + translation_model if use_translation else 'OFF'}")
94
 
95
  if use_translation:
96
  # Translate user message → English before sending to main model
97
+ logger.debug(f"[Chat] Translating user message: {message[:80]!r}")
98
  en_message = translate_to_english(message, language, client, translation_model)
99
+ logger.debug(f"[Chat] User message (EN): {en_message[:80]!r}")
100
  prompt_language = "English" # main model always responds in English
101
  else:
102
  en_message = message
 
112
  current_message=en_message,
113
  language=prompt_language,
114
  )
115
+ logger.debug(
116
+ f"[Chat] Prompt built: {len(messages[0]['content'])} chars system | "
117
+ f"{len(messages)} total messages"
118
+ )
119
 
120
  # ── LLM call ──────────────────────────────────────────────────────────────
121
+ logger.info(f"[Chat] LLM call: model='{model}' max_tokens={max_tokens} temp={temperature:.2f} top_p={top_p:.2f}")
122
 
123
  raw_response = ""
124
 
 
153
  yield raw_response
154
 
155
  except Exception as e:
156
+ logger.error(f"[Chat] LLM error: {e}", exc_info=True)
157
  error_note = "\n\n*(The model did not respond. Please try again.)*"
158
  yield (raw_response + error_note) if raw_response else error_note.strip()
159
  return
160
 
161
+ logger.info(f"[Chat] Stream complete: {len(raw_response)} chars generated")
162
+
163
  # ── Post-stream ────────────────────────────────────────────────────────────
164
  # Memory always stores English (so main model gets consistent context)
165
  try:
166
  append_message(character_id, "user", en_message)
167
  append_message(character_id, "assistant", raw_response)
168
+ logger.debug(f"[Chat] Memory saved: user + assistant turn appended")
169
  except Exception as e:
170
+ logger.error(f"[Chat] Memory save failed: {e}", exc_info=True)
171
 
172
  # ── Translate response → user's language, format, yield ───────────────────
173
  if use_translation:
174
+ logger.debug(f"[Chat] Translating response → {language} ({len(raw_response)} chars)")
175
  final_text = translate_from_english(raw_response, language, client, translation_model)
176
  else:
177
  final_text = raw_response
178
 
179
  segments = parse_response(final_text)
180
+ logger.debug(f"[Chat] Parser: {len(segments)} segments → {[s['type'] for s in segments]}")
181
  formatted = segments_to_markdown(segments)
182
+ logger.info(f"[Chat] ✓ Response ready: {len(formatted)} chars, {len(segments)} segments")
183
  yield formatted
engine/memory.py CHANGED
@@ -58,13 +58,17 @@ def load_recent_messages(character_id: str, max_messages: int = MAX_RECENT) -> l
58
  """Load the last `max_messages` conversation turns for a character."""
59
  path = os.path.join(_memory_dir(character_id), "recent_messages.json")
60
  data = _load(path, {"messages": []})
61
- return data.get("messages", [])[-max_messages:]
 
 
62
 
63
 
64
  def save_recent_messages(character_id: str, messages: list[dict]) -> None:
65
  """Overwrite the recent messages file (trims to MAX_RECENT)."""
66
  path = os.path.join(_memory_dir(character_id), "recent_messages.json")
67
- _save(path, {"messages": messages[-MAX_RECENT:]})
 
 
68
 
69
 
70
  def append_message(character_id: str, role: str, content: str) -> None:
@@ -72,6 +76,7 @@ def append_message(character_id: str, role: str, content: str) -> None:
72
  messages = load_recent_messages(character_id, max_messages=MAX_RECENT)
73
  messages.append({"role": role, "content": content})
74
  save_recent_messages(character_id, messages)
 
75
 
76
 
77
  # ── Summary ────────────────────────────────────────────────────────────────────
@@ -80,13 +85,16 @@ def load_summary(character_id: str) -> str:
80
  """Load the rolling conversation summary string for a character."""
81
  path = os.path.join(_memory_dir(character_id), "summary.json")
82
  data = _load(path, {"summary": ""})
83
- return data.get("summary", "")
 
 
84
 
85
 
86
  def save_summary(character_id: str, summary: str) -> None:
87
  """Overwrite the conversation summary for a character."""
88
  path = os.path.join(_memory_dir(character_id), "summary.json")
89
  _save(path, {"summary": summary, "updated_at": _now()})
 
90
 
91
 
92
  # ── Long-term memory ───────────────────────────────────────────────────────────
@@ -95,13 +103,16 @@ def load_long_term_memory(character_id: str) -> list[dict]:
95
  """Load all long-term memory entries for a character."""
96
  path = os.path.join(_memory_dir(character_id), "long_term_memory.json")
97
  data = _load(path, {"memories": []})
98
- return data.get("memories", [])
 
 
99
 
100
 
101
  def save_long_term_memory(character_id: str, memories: list[dict]) -> None:
102
  """Overwrite the long-term memory file for a character."""
103
  path = os.path.join(_memory_dir(character_id), "long_term_memory.json")
104
  _save(path, {"memories": memories})
 
105
 
106
 
107
  def add_memory(character_id: str, content: str, importance: str = "medium") -> None:
@@ -112,4 +123,5 @@ def add_memory(character_id: str, content: str, importance: str = "medium") -> N
112
  "importance": importance,
113
  "created_at": _now(),
114
  })
115
- save_long_term_memory(character_id, memories)
 
 
58
  """Load the last `max_messages` conversation turns for a character."""
59
  path = os.path.join(_memory_dir(character_id), "recent_messages.json")
60
  data = _load(path, {"messages": []})
61
+ messages = data.get("messages", [])[-max_messages:]
62
+ logger.debug(f"[Memory] Loaded {len(messages)} recent messages for '{character_id}'")
63
+ return messages
64
 
65
 
66
  def save_recent_messages(character_id: str, messages: list[dict]) -> None:
67
  """Overwrite the recent messages file (trims to MAX_RECENT)."""
68
  path = os.path.join(_memory_dir(character_id), "recent_messages.json")
69
+ trimmed = messages[-MAX_RECENT:]
70
+ _save(path, {"messages": trimmed})
71
+ logger.debug(f"[Memory] Saved {len(trimmed)} recent messages for '{character_id}'")
72
 
73
 
74
  def append_message(character_id: str, role: str, content: str) -> None:
 
76
  messages = load_recent_messages(character_id, max_messages=MAX_RECENT)
77
  messages.append({"role": role, "content": content})
78
  save_recent_messages(character_id, messages)
79
+ logger.debug(f"[Memory] Appended [{role}] message for '{character_id}': {content[:60]!r}")
80
 
81
 
82
  # ── Summary ────────────────────────────────────────────────────────────────────
 
85
  """Load the rolling conversation summary string for a character."""
86
  path = os.path.join(_memory_dir(character_id), "summary.json")
87
  data = _load(path, {"summary": ""})
88
+ summary = data.get("summary", "")
89
+ logger.debug(f"[Memory] Summary for '{character_id}': {len(summary)} chars")
90
+ return summary
91
 
92
 
93
  def save_summary(character_id: str, summary: str) -> None:
94
  """Overwrite the conversation summary for a character."""
95
  path = os.path.join(_memory_dir(character_id), "summary.json")
96
  _save(path, {"summary": summary, "updated_at": _now()})
97
+ logger.info(f"[Memory] Summary saved for '{character_id}': {len(summary)} chars")
98
 
99
 
100
  # ── Long-term memory ───────────────────────────────────────────────────────────
 
103
  """Load all long-term memory entries for a character."""
104
  path = os.path.join(_memory_dir(character_id), "long_term_memory.json")
105
  data = _load(path, {"memories": []})
106
+ memories = data.get("memories", [])
107
+ logger.debug(f"[Memory] Loaded {len(memories)} long-term memories for '{character_id}'")
108
+ return memories
109
 
110
 
111
  def save_long_term_memory(character_id: str, memories: list[dict]) -> None:
112
  """Overwrite the long-term memory file for a character."""
113
  path = os.path.join(_memory_dir(character_id), "long_term_memory.json")
114
  _save(path, {"memories": memories})
115
+ logger.info(f"[Memory] Saved {len(memories)} long-term memories for '{character_id}'")
116
 
117
 
118
  def add_memory(character_id: str, content: str, importance: str = "medium") -> None:
 
123
  "importance": importance,
124
  "created_at": _now(),
125
  })
126
+ save_long_term_memory(character_id, memories)
127
+ logger.info(f"[Memory] New long-term memory [{importance}] for '{character_id}': {content[:80]!r}")
engine/parser.py CHANGED
@@ -20,9 +20,12 @@ Example:
20
  # ]
21
  """
22
 
 
23
  import re
24
  from typing import TypedDict
25
 
 
 
26
 
27
  # Matches:
28
  # <action>...</action>
@@ -87,6 +90,8 @@ def parse_response(text: str) -> list[Segment]:
87
  "content": part,
88
  })
89
 
 
 
90
  return segments
91
 
92
 
 
20
  # ]
21
  """
22
 
23
+ import logging
24
  import re
25
  from typing import TypedDict
26
 
27
+ logger = logging.getLogger(__name__)
28
+
29
 
30
  # Matches:
31
  # <action>...</action>
 
90
  "content": part,
91
  })
92
 
93
+ type_counts = {t: sum(1 for s in segments if s["type"] == t) for t in ("dialogue", "action", "thought")}
94
+ logger.debug(f"[Parser] {len(text)} chars → {len(segments)} segments {type_counts}")
95
  return segments
96
 
97
 
engine/prompt_builder.py CHANGED
@@ -305,4 +305,10 @@ def build_messages(
305
  messages.extend(recent_messages)
306
  messages.append({"role": "user", "content": current_message})
307
 
 
 
 
 
 
 
308
  return messages
 
305
  messages.extend(recent_messages)
306
  messages.append({"role": "user", "content": current_message})
307
 
308
+ logger.debug(
309
+ f"[PromptBuilder] Built: {len(system_content)} chars system | "
310
+ f"{len(system_sections)} sections | "
311
+ f"{len(recent_messages)} history turns | "
312
+ f"language='{language}'"
313
+ )
314
  return messages
engine/suggestions.py CHANGED
@@ -85,11 +85,13 @@ def generate_suggestions(
85
  client: Initialised HuggingFace InferenceClient.
86
  model: HuggingFace model string.
87
  """
 
 
 
88
  try:
89
  system_prompt = _load_prompt()
90
 
91
  # Prepend a brief character context so suggestions fit the scene
92
- name = character.get("name", "the character")
93
  personality = character.get("personality", [])
94
  char_ctx = f"The user is talking with {name}"
95
  if personality:
@@ -110,7 +112,9 @@ def generate_suggestions(
110
  )
111
 
112
  raw = response.choices[0].message.content
113
- return _parse(raw)
 
 
114
 
115
  except Exception as e:
116
  logger.warning(f"[Suggestions] Generation failed ({e}). Using fallback.")
 
85
  client: Initialised HuggingFace InferenceClient.
86
  model: HuggingFace model string.
87
  """
88
+ name = character.get("name", "the character")
89
+ logger.info(f"[Suggestions] ▶ character='{name}' | model='{model}' | {len(recent_messages)} recent messages")
90
+
91
  try:
92
  system_prompt = _load_prompt()
93
 
94
  # Prepend a brief character context so suggestions fit the scene
 
95
  personality = character.get("personality", [])
96
  char_ctx = f"The user is talking with {name}"
97
  if personality:
 
112
  )
113
 
114
  raw = response.choices[0].message.content
115
+ suggestions = _parse(raw)
116
+ logger.info(f"[Suggestions] ✓ Generated: {[s['tone'] for s in suggestions]}")
117
+ return suggestions
118
 
119
  except Exception as e:
120
  logger.warning(f"[Suggestions] Generation failed ({e}). Using fallback.")
engine/translator.py CHANGED
@@ -10,6 +10,7 @@ Formatting rules:
10
  """
11
 
12
  import logging
 
13
  from huggingface_hub import InferenceClient
14
 
15
  logger = logging.getLogger(__name__)
@@ -42,8 +43,11 @@ def translate_to_english(
42
  Returns the original text unchanged if already English or on any failure.
43
  """
44
  if not text.strip() or source_language.strip().lower() == "english":
 
45
  return text
46
 
 
 
47
  try:
48
  response = client.chat_completion(
49
  messages=[
@@ -56,10 +60,12 @@ def translate_to_english(
56
  temperature=0.1,
57
  )
58
  result = response.choices[0].message.content.strip()
59
- logger.info(f"[Translator] {source_language}→EN: {text[:60]!r} → {result[:60]!r}")
 
 
60
  return result
61
  except Exception as e:
62
- logger.warning(f"[Translator] to_english failed ({e}), keeping original.")
63
  return text
64
 
65
 
@@ -74,8 +80,11 @@ def translate_from_english(
74
  Returns the original text unchanged if target is English or on any failure.
75
  """
76
  if not text.strip() or target_language.strip().lower() == "english":
 
77
  return text
78
 
 
 
79
  try:
80
  system = _FROM_EN.format(language=target_language)
81
  response = client.chat_completion(
@@ -89,8 +98,10 @@ def translate_from_english(
89
  temperature=0.1,
90
  )
91
  result = response.choices[0].message.content.strip()
92
- logger.info(f"[Translator] EN→{target_language}: {text[:60]!r} → {result[:60]!r}")
 
 
93
  return result
94
  except Exception as e:
95
- logger.warning(f"[Translator] from_english failed ({e}), keeping original.")
96
  return text
 
10
  """
11
 
12
  import logging
13
+ import time
14
  from huggingface_hub import InferenceClient
15
 
16
  logger = logging.getLogger(__name__)
 
43
  Returns the original text unchanged if already English or on any failure.
44
  """
45
  if not text.strip() or source_language.strip().lower() == "english":
46
+ logger.debug("[Translator] to_english skipped (already English or empty).")
47
  return text
48
 
49
+ logger.info(f"[Translator] ▶ {source_language}→EN | model='{model}' | {len(text)} chars")
50
+ start = time.time()
51
  try:
52
  response = client.chat_completion(
53
  messages=[
 
60
  temperature=0.1,
61
  )
62
  result = response.choices[0].message.content.strip()
63
+ elapsed = time.time() - start
64
+ logger.info(f"[Translator] ✓ {source_language}→EN | {elapsed:.1f}s | {len(result)} chars")
65
+ logger.debug(f"[Translator] {source_language}→EN: {text[:80]!r} → {result[:80]!r}")
66
  return result
67
  except Exception as e:
68
+ logger.error(f"[Translator] to_english FAILED | model='{model}' | error: {e}", exc_info=True)
69
  return text
70
 
71
 
 
80
  Returns the original text unchanged if target is English or on any failure.
81
  """
82
  if not text.strip() or target_language.strip().lower() == "english":
83
+ logger.debug("[Translator] from_english skipped (target is English or empty).")
84
  return text
85
 
86
+ logger.info(f"[Translator] ▶ EN→{target_language} | model='{model}' | {len(text)} chars")
87
+ start = time.time()
88
  try:
89
  system = _FROM_EN.format(language=target_language)
90
  response = client.chat_completion(
 
98
  temperature=0.1,
99
  )
100
  result = response.choices[0].message.content.strip()
101
+ elapsed = time.time() - start
102
+ logger.info(f"[Translator] ✓ EN→{target_language} | {elapsed:.1f}s | {len(result)} chars")
103
+ logger.debug(f"[Translator] EN→{target_language}: {text[:80]!r} → {result[:80]!r}")
104
  return result
105
  except Exception as e:
106
+ logger.error(f"[Translator] from_english FAILED | model='{model}' | error: {e}", exc_info=True)
107
  return text