Skip to content

Commit 6d62bbc

Browse files
committed
Add translation validation to graph.py to match translate_only.py
- Validate translation result is not None - Validate translation result is not empty/whitespace - Validate cleaned content is not empty - Improve error messages with context information - Ensure consistent behavior between translate_only.py and graph.py
1 parent b75c912 commit 6d62bbc

File tree

3 files changed

+90
-8
lines changed

3 files changed

+90
-8
lines changed

tools/ai-markmap-agent/src/agents/translator.py

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,9 @@ def _load_translation_prompt(self, prompt_file: Path) -> str:
8181
"""Load translation prompt from file with caching."""
8282
key = str(prompt_file)
8383
if key not in self._prompt_cache:
84-
if prompt_file.exists():
85-
self._prompt_cache[key] = prompt_file.read_text(encoding="utf-8")
86-
else:
87-
raise FileNotFoundError(f"Prompt file not found: {prompt_file}")
84+
if not prompt_file.exists():
85+
raise FileNotFoundError(f"Translation prompt file not found: {prompt_file}")
86+
self._prompt_cache[key] = prompt_file.read_text(encoding="utf-8")
8887
return self._prompt_cache[key]
8988

9089
def translate(self, content: str, output_type: str) -> str:
@@ -108,6 +107,14 @@ def translate(self, content: str, output_type: str) -> str:
108107
self.target_language
109108
)
110109

110+
# Validate prompt template
111+
if not prompt_template or len(prompt_template.strip()) == 0:
112+
raise ValueError(
113+
f"Translation prompt template is empty. "
114+
f"Target language: {self.target_language}, "
115+
f"Prompt file: {ZH_TW_PROMPT_FILE if self.target_language == 'zh-TW' else GENERIC_PROMPT_FILE}"
116+
)
117+
111118
# Build full prompt with content
112119
prompt = f"""{prompt_template}
113120
@@ -117,17 +124,63 @@ def translate(self, content: str, output_type: str) -> str:
117124
118125
{content}"""
119126

127+
# Validate full prompt
128+
if not prompt or len(prompt.strip()) == 0:
129+
raise ValueError(
130+
f"Built prompt is empty. "
131+
f"Template length: {len(prompt_template)}, "
132+
f"Content length: {len(content)}"
133+
)
134+
120135
messages = self._build_messages(prompt)
121136

122137
# Save LLM input
123138
self._save_llm_call_input(messages, "translate")
124139

140+
# Call LLM
125141
response = self.llm.invoke(messages)
126142

143+
# Validate response
144+
if response is None:
145+
raise ValueError(
146+
f"LLM returned None response. "
147+
f"Model: {self.model_config.get('model')}, "
148+
f"Source: {self.source_language} → Target: {self.target_language}"
149+
)
150+
151+
# Extract content from response
152+
# Handle different response types (AIMessage, str, etc.)
153+
if hasattr(response, 'content'):
154+
content = response.content
155+
elif isinstance(response, str):
156+
content = response
157+
else:
158+
# Try to get content via dict access or other methods
159+
try:
160+
content = str(response)
161+
except Exception as e:
162+
raise ValueError(
163+
f"Unable to extract content from response. "
164+
f"Response type: {type(response)}, "
165+
f"Error: {e}"
166+
)
167+
168+
# Validate content
169+
if content is None:
170+
raise ValueError(
171+
f"LLM response content is None. "
172+
f"Model: {self.model_config.get('model')}, "
173+
f"Source: {self.source_language} → Target: {self.target_language}. "
174+
f"Check API response in debug output files."
175+
)
176+
177+
# Convert to string if needed
178+
content_str = str(content) if not isinstance(content, str) else content
179+
127180
# Save LLM output
128-
self._save_llm_call_output(response.content, "translate")
181+
self._save_llm_call_output(content_str, "translate")
129182

130-
return response.content
183+
return content_str
131184

132185

133186
def create_translators(config: dict[str, Any] | None = None) -> list[dict[str, Any]]:

tools/ai-markmap-agent/src/graph.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -766,16 +766,41 @@ def run_translations(state: WorkflowState) -> WorkflowState:
766766
debug.save_translation(content, output_key, target_key, is_before=True)
767767

768768
translated_content = translator.translate(content, "general")
769+
770+
# Validate translation result
771+
if not translated_content:
772+
raise ValueError(
773+
f"Translation returned empty content (None). "
774+
f"Source: {source_lang} → Target: {target_lang}, "
775+
f"Model: {model}, Output: {output_key}"
776+
)
777+
if len(translated_content.strip()) == 0:
778+
raise ValueError(
779+
f"Translation returned only whitespace. "
780+
f"Source: {source_lang} → Target: {target_lang}, "
781+
f"Model: {model}, Output: {output_key}"
782+
)
783+
769784
# Clean up LLM artifacts
770785
translated_content = clean_translated_content(translated_content)
786+
787+
# Validate cleaned content
788+
if not translated_content or len(translated_content.strip()) == 0:
789+
raise ValueError(
790+
f"After cleaning, translation is empty. "
791+
f"Source: {source_lang} → Target: {target_lang}, "
792+
f"Model: {model}, Output: {output_key}"
793+
)
794+
771795
translated[target_key] = translated_content
772796
print(f" ✓ Translated: {output_key}{target_key}")
773797

774798
if debug.enabled:
775799
debug.save_translation(translated_content, output_key, target_key, is_before=False)
776800
except Exception as e:
777-
print(f" ✗ Translation failed: {e}")
778-
state["errors"].append(f"Translation error: {e}")
801+
error_msg = f"Translation failed for {output_key}{target_lang}: {e}"
802+
print(f" ✗ {error_msg}")
803+
state["errors"].append(error_msg)
779804

780805
state["translated_outputs"] = translated
781806
return state

tools/ai-markmap-agent/translate_only.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ def translate_file(
7979
print(f"🌐 Translation: {source_lang}{target_lang}")
8080
print(f"🤖 Model: {model}")
8181

82+
# Check if input file exists
83+
if not input_path.exists():
84+
raise FileNotFoundError(f"Input file not found: {input_path}")
85+
8286
# Read input
8387
content = input_path.read_text(encoding="utf-8")
8488
print(f" Read {len(content)} chars, {len(content.splitlines())} lines")

0 commit comments

Comments
 (0)