Skip to content

Add translation tracking - #602

Open
mcmah309 wants to merge 1 commit into
funstory-ai:mainfrom
mcmah309:translation_tracking
Open

Add translation tracking#602
mcmah309 wants to merge 1 commit into
funstory-ai:mainfrom
mcmah309:translation_tracking

Conversation

@mcmah309

@mcmah309 mcmah309 commented Aug 1, 2026

Copy link
Copy Markdown

PR Title

Adds translation tracking to the api so you can see how translations were mapped between the documents. Enabled through an opt-in config flag.

PR Type

  • ✨ Enhancement
  • 🐛 Bug Fix
  • 📚 Documentation
  • 🏗️ Refactor
  • 🧪 Test
  • 🧹 Chore

Breaking Changes

Contributor Checklist

  • I have fully read and understood the CONTRIBUTING.md guide.
  • I have performed a self-review of my own code.
  • My changes follow the project's code style and guidelines
  • I have linked the related issue(s) in the description above (if applicable)
  • I have updated relevant documentation (if applicable)
  • I have added necessary tests that prove my fix is effective or that my feature works (if applicable)
  • All new and existing tests passed locally with my changes
  • My changes generate no new warnings or errors
  • I understand that due to limited maintainer resources, only small PRs are accepted. Suggestions with proof-of-concept patches are appreciated, and my patch may be rewritten if necessary.

Testing Instructions

Screenshots (if applicable)

Additional Notes


Summary by cubic

Add translation tracking to the PDF translation pipeline. It maps source paragraphs to outputs with geometry and layout info, enabled via an opt-in flag.

  • New Features

    • Added enable_translation_tracking in TranslationConfig to collect tracking data.
    • Tracking includes page, cross-page, and cross-column paragraphs with input/output text, page_number, box, layout_label, placeholders, and optional per-character char_boxes.
    • Tracking is attached to TranslateResult.translation_tracking.
    • Page numbers are rebased to document indices when translating split parts.
    • Merged results now concatenate tracking across parts and renumber multi_paragraph_id to be unique.
  • Migration

    • Enable with enable_translation_tracking=True in TranslationConfig.
    • Read tracking from TranslateResult.translation_tracking (present only when enabled).

Written for commit ac34156. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="babeldoc/format/pdf/translation_config.py">

<violation number="1" location="babeldoc/format/pdf/translation_config.py:221">
P1: The executor request cannot enable translation tracking because `build_translation_config` never forwards `enable_translation_tracking` into `TranslationConfig`. Wiring the request field through, with a false default when absent, would make the opt-in reachable through the API.</violation>

<violation number="2" location="babeldoc/format/pdf/translation_config.py:381">
P2: Leaving tracking disabled still allocates paragraph/LLM tracking data and writes `translate_tracking.json` for normal translations, so the opt-in flag does not prevent its memory and disk overhead. Gating tracker creation and serialization on `enable_translation_tracking` would preserve the intended opt-in behavior.</violation>

<violation number="3" location="babeldoc/format/pdf/translation_config.py:534">
P1: The executor API never returns translation mappings even when `TranslateResult.translation_tracking` is populated, so the advertised tracking data is unavailable to API callers. Including `translation_tracking` in the payload when tracking is enabled would expose the new result data.</violation>
</file>

<file name="babeldoc/format/pdf/document_il/midend/il_translator.py">

<violation number="1" location="babeldoc/format/pdf/document_il/midend/il_translator.py:192">
P3: The new translation-tracking response is unverified for both `enable_translation_tracking=False` and `True`, including the new geometry and per-character fields; a regression could silently break API consumers. Adding focused serialization tests and documenting the opt-in test workflow would make the rollout verifiable.</violation>
</file>

<file name="babeldoc/format/pdf/document_il/midend/il_translator_llm_only.py">

<violation number="1" location="babeldoc/format/pdf/document_il/midend/il_translator_llm_only.py:182">
P3: The new LLM-only tracking path is not covered by a regression test, so the opt-in API output can silently regress without detection. A focused test could enable `enable_translation_tracking`, run a minimal LLM translation, and assert returned page/cross-page records include geometry and `layout_label`; the PR should also document how to exercise this flag.</violation>
</file>

<file name="babeldoc/format/pdf/high_level.py">

<violation number="1" location="babeldoc/format/pdf/high_level.py:1017">
P2: Normal translations now retain the entire translation-tracking object until `_do_translate_single` finishes, even when tracking is disabled, increasing peak memory usage for large PDFs and undermining the opt-in behavior. Capturing the tracker only when `enable_translation_tracking` is enabled would preserve the previous memory lifecycle.</violation>
</file>

<file name="babeldoc/format/pdf/result_merger.py">

<violation number="1" location="babeldoc/format/pdf/result_merger.py:201">
P3: Merging currently mutates and aliases the input parts' tracking data: callers retaining a part result will observe its batch IDs rewritten, and later edits to the merged tracking can modify that part. Copy each part's tracking structure before renumbering and appending it so aggregation does not change its inputs.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

metadata_extra_data: str | None = None,
term_pool_max_workers: int | None = None,
disable_same_text_fallback: bool = False,
enable_translation_tracking: bool = False,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The executor request cannot enable translation tracking because build_translation_config never forwards enable_translation_tracking into TranslationConfig. Wiring the request field through, with a false default when absent, would make the opt-in reachable through the API.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At babeldoc/format/pdf/translation_config.py, line 221:

<comment>The executor request cannot enable translation tracking because `build_translation_config` never forwards `enable_translation_tracking` into `TranslationConfig`. Wiring the request field through, with a false default when absent, would make the opt-in reachable through the API.</comment>

<file context>
@@ -217,6 +218,7 @@ def __init__(
         metadata_extra_data: str | None = None,
         term_pool_max_workers: int | None = None,
         disable_same_text_fallback: bool = False,
+        enable_translation_tracking: bool = False,
     ):
         self.translator = translator
</file context>

auto_extracted_glossary_path: Path | None
total_valid_character_count: int | None
total_valid_text_token_count: int | None
translation_tracking: dict | None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The executor API never returns translation mappings even when TranslateResult.translation_tracking is populated, so the advertised tracking data is unavailable to API callers. Including translation_tracking in the payload when tracking is enabled would expose the new result data.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At babeldoc/format/pdf/translation_config.py, line 534:

<comment>The executor API never returns translation mappings even when `TranslateResult.translation_tracking` is populated, so the advertised tracking data is unavailable to API callers. Including `translation_tracking` in the payload when tracking is enabled would expose the new result data.</comment>

<file context>
@@ -517,6 +531,7 @@ class TranslateResult:
     auto_extracted_glossary_path: Path | None
     total_valid_character_count: int | None
     total_valid_text_token_count: int | None
+    translation_tracking: dict | None
 
     def __init__(
</file context>

"cache_hit_prompt_tokens": 0,
}
self.disable_same_text_fallback = disable_same_text_fallback
self.enable_translation_tracking = enable_translation_tracking

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Leaving tracking disabled still allocates paragraph/LLM tracking data and writes translate_tracking.json for normal translations, so the opt-in flag does not prevent its memory and disk overhead. Gating tracker creation and serialization on enable_translation_tracking would preserve the intended opt-in behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At babeldoc/format/pdf/translation_config.py, line 381:

<comment>Leaving tracking disabled still allocates paragraph/LLM tracking data and writes `translate_tracking.json` for normal translations, so the opt-in flag does not prevent its memory and disk overhead. Gating tracker creation and serialization on `enable_translation_tracking` would preserve the intended opt-in behavior.</comment>

<file context>
@@ -376,6 +378,7 @@ def __init__(
             "cache_hit_prompt_tokens": 0,
         }
         self.disable_same_text_fallback = disable_same_text_fallback
+        self.enable_translation_tracking = enable_translation_tracking
 
         if self.ocr_workaround:
</file context>

il_translator = ILTranslator(translate_engine, translation_config)

il_translator.translate(docs)
translation_tracker = il_translator.translate(docs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Normal translations now retain the entire translation-tracking object until _do_translate_single finishes, even when tracking is disabled, increasing peak memory usage for large PDFs and undermining the opt-in behavior. Capturing the tracker only when enable_translation_tracking is enabled would preserve the previous memory lifecycle.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At babeldoc/format/pdf/high_level.py, line 1017:

<comment>Normal translations now retain the entire translation-tracking object until `_do_translate_single` finishes, even when tracking is disabled, increasing peak memory usage for large PDFs and undermining the opt-in behavior. Capturing the tracker only when `enable_translation_tracking` is enabled would preserve the previous memory lifecycle.</comment>

<file context>
@@ -994,13 +1007,14 @@ def _do_translate_single(
             il_translator = ILTranslator(translate_engine, translation_config)
 
-        il_translator.translate(docs)
+        translation_tracker = il_translator.translate(docs)
         del il_translator
         logger.debug(f"finish ILTranslator from {temp_pdf_path}")
</file context>
Suggested change
translation_tracker = il_translator.translate(docs)
if translation_config.enable_translation_tracking:
translation_tracker = il_translator.translate(docs)
else:
il_translator.translate(docs)

return page

def to_json(self):
def to_dict(self) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The new translation-tracking response is unverified for both enable_translation_tracking=False and True, including the new geometry and per-character fields; a regression could silently break API consumers. Adding focused serialization tests and documenting the opt-in test workflow would make the rollout verifiable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At babeldoc/format/pdf/document_il/midend/il_translator.py, line 192:

<comment>The new translation-tracking response is unverified for both `enable_translation_tracking=False` and `True`, including the new geometry and per-character fields; a regression could silently break API consumers. Adding focused serialization tests and documenting the opt-in test workflow would make the rollout verifiable.</comment>

<file context>
@@ -164,7 +189,7 @@ def new_cross_column(self):
         return page
 
-    def to_json(self):
+    def to_dict(self) -> dict:
         pages = []
         for page in self.page:
</file context>

def translate(self, docs: Document) -> DocumentTranslateTracker:
self.il_translator.docs = docs
tracker = DocumentTranslateTracker()
tracker = DocumentTranslateTracker(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The new LLM-only tracking path is not covered by a regression test, so the opt-in API output can silently regress without detection. A focused test could enable enable_translation_tracking, run a minimal LLM translation, and assert returned page/cross-page records include geometry and layout_label; the PR should also document how to exercise this flag.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At babeldoc/format/pdf/document_il/midend/il_translator_llm_only.py, line 182:

<comment>The new LLM-only tracking path is not covered by a regression test, so the opt-in API output can silently regress without detection. A focused test could enable `enable_translation_tracking`, run a minimal LLM translation, and assert returned page/cross-page records include geometry and `layout_label`; the PR should also document how to exercise this flag.</comment>

<file context>
@@ -172,9 +177,11 @@ def find_title_paragraph(self, docs: Document) -> PdfParagraph | None:
+    def translate(self, docs: Document) -> DocumentTranslateTracker:
         self.il_translator.docs = docs
-        tracker = DocumentTranslateTracker()
+        tracker = DocumentTranslateTracker(
+            char_boxes=self.translation_config.enable_translation_tracking,
+        )
</file context>

if batch_id not in batch_ids:
batch_ids[batch_id] = next_batch_id
next_batch_id += 1
paragraph["multi_paragraph_id"] = batch_ids[batch_id]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Merging currently mutates and aliases the input parts' tracking data: callers retaining a part result will observe its batch IDs rewritten, and later edits to the merged tracking can modify that part. Copy each part's tracking structure before renumbering and appending it so aggregation does not change its inputs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At babeldoc/format/pdf/result_merger.py, line 201:

<comment>Merging currently mutates and aliases the input parts' tracking data: callers retaining a part result will observe its batch IDs rewritten, and later edits to the merged tracking can modify that part. Copy each part's tracking structure before renumbering and appending it so aggregation does not change its inputs.</comment>

<file context>
@@ -170,6 +176,33 @@ def merge_results(
+                if batch_id not in batch_ids:
+                    batch_ids[batch_id] = next_batch_id
+                    next_batch_id += 1
+                paragraph["multi_paragraph_id"] = batch_ids[batch_id]
+            for section in TRACKING_SECTIONS:
+                merged[section].extend(tracking[section])
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant