Spaces:
Running
Running
Step 4A-6-C-R3.2.3 Translation Marker Robustness Fix
#13
by GuXiaopeng - opened
- __pycache__/app.cpython-313.pyc +0 -0
- __pycache__/config.cpython-313.pyc +0 -0
- __pycache__/i18n.cpython-313.pyc +0 -0
- __pycache__/managed_auth.cpython-313.pyc +0 -0
- __pycache__/task_executor.cpython-313.pyc +0 -0
- core/__pycache__/checkpoint_store.cpython-313.pyc +0 -0
- core/__pycache__/generation_runner.cpython-313.pyc +0 -0
- core/__pycache__/meta_extractor.cpython-313.pyc +0 -0
- core/__pycache__/pipeline_agent.cpython-313.pyc +2 -2
- core/__pycache__/production_guard.cpython-313.pyc +0 -0
- core/__pycache__/qualitative_engine.cpython-313.pyc +0 -0
- core/__pycache__/quantitative_engine.cpython-313.pyc +0 -0
- core/__pycache__/rag_engine.cpython-313.pyc +0 -0
- core/pipeline_agent.py +89 -18
- smartpaper_restore_bridge/__pycache__/__init__.cpython-313.pyc +0 -0
- utils/__pycache__/docx_builder.cpython-313.pyc +0 -0
- utils/__pycache__/file_parser.cpython-313.pyc +0 -0
__pycache__/app.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ
|
|
|
__pycache__/config.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/config.cpython-313.pyc and b/__pycache__/config.cpython-313.pyc differ
|
|
|
__pycache__/i18n.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/i18n.cpython-313.pyc and b/__pycache__/i18n.cpython-313.pyc differ
|
|
|
__pycache__/managed_auth.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/managed_auth.cpython-313.pyc and b/__pycache__/managed_auth.cpython-313.pyc differ
|
|
|
__pycache__/task_executor.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/task_executor.cpython-313.pyc and b/__pycache__/task_executor.cpython-313.pyc differ
|
|
|
core/__pycache__/checkpoint_store.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/checkpoint_store.cpython-313.pyc and b/core/__pycache__/checkpoint_store.cpython-313.pyc differ
|
|
|
core/__pycache__/generation_runner.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/generation_runner.cpython-313.pyc and b/core/__pycache__/generation_runner.cpython-313.pyc differ
|
|
|
core/__pycache__/meta_extractor.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/meta_extractor.cpython-313.pyc and b/core/__pycache__/meta_extractor.cpython-313.pyc differ
|
|
|
core/__pycache__/pipeline_agent.cpython-313.pyc
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:576dd3905d2ec79e768e26b13790a53de0568073c16ef7e03c9dda6fcc26d5f7
|
| 3 |
+
size 158942
|
core/__pycache__/production_guard.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/production_guard.cpython-313.pyc and b/core/__pycache__/production_guard.cpython-313.pyc differ
|
|
|
core/__pycache__/qualitative_engine.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/qualitative_engine.cpython-313.pyc and b/core/__pycache__/qualitative_engine.cpython-313.pyc differ
|
|
|
core/__pycache__/quantitative_engine.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/quantitative_engine.cpython-313.pyc and b/core/__pycache__/quantitative_engine.cpython-313.pyc differ
|
|
|
core/__pycache__/rag_engine.cpython-313.pyc
CHANGED
|
Binary files a/core/__pycache__/rag_engine.cpython-313.pyc and b/core/__pycache__/rag_engine.cpython-313.pyc differ
|
|
|
core/pipeline_agent.py
CHANGED
|
@@ -2074,7 +2074,6 @@ Return ONLY a valid JSON array of strings."""
|
|
| 2074 |
|
| 2075 |
cfg = self.citation_semantic_audit
|
| 2076 |
enabled = bool(cfg.get("enabled", True))
|
| 2077 |
-
fail_closed = bool(cfg.get("fail_closed", True))
|
| 2078 |
spans = self._citation_sentence_spans(text)
|
| 2079 |
if not spans:
|
| 2080 |
return text
|
|
@@ -2282,6 +2281,11 @@ Return exactly:
|
|
| 2282 |
f"[{tag.upper()}]"
|
| 2283 |
for tag in re.findall(r"CITE-\d+", revised, flags=re.IGNORECASE)
|
| 2284 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2285 |
disallowed = revised_tags - set(record["allowed_tags"])
|
| 2286 |
if disallowed:
|
| 2287 |
raise DeepSeekRequestError(
|
|
@@ -2469,6 +2473,8 @@ Return exactly:
|
|
| 2469 |
except PipelineCancelledError:
|
| 2470 |
raise
|
| 2471 |
except CitationAuditError as error:
|
|
|
|
|
|
|
| 2472 |
logging.warning(
|
| 2473 |
'Citation semantic audit technical fallback: section=%s code=%s detail=%s',
|
| 2474 |
section_name,
|
|
@@ -2477,6 +2483,8 @@ Return exactly:
|
|
| 2477 |
)
|
| 2478 |
value = self._local_citation_recovery(value, section_name)
|
| 2479 |
except Exception as error:
|
|
|
|
|
|
|
| 2480 |
logging.warning(
|
| 2481 |
'Citation semantic audit fallback: section=%s error=%s',
|
| 2482 |
section_name,
|
|
@@ -2806,6 +2814,83 @@ Translate headings and terminology naturally. Preserve standard variable abbrevi
|
|
| 2806 |
if not is_chinese and not re.search(r'[А-Яа-яЁёӘәҒғҚқҢңӨөҰұҮүҺһІі]', translated):
|
| 2807 |
raise DeepSeekRequestError(f'Translation block {block_id} contains no Cyrillic target-language text.')
|
| 2808 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2809 |
def translate_locked_manuscript(self, manuscript, target_lang):
|
| 2810 |
value = str(manuscript or '').strip()
|
| 2811 |
if not value:
|
|
@@ -2839,24 +2924,10 @@ Translate headings and terminology naturally. Preserve standard variable abbrevi
|
|
| 2839 |
system_prompt = self._translation_prompt(target_lang) + '\nUse this terminology glossary consistently:\n' + json.dumps(glossary, ensure_ascii=False)
|
| 2840 |
for batch in batches:
|
| 2841 |
self._check_cancelled()
|
| 2842 |
-
|
| 2843 |
-
|
| 2844 |
-
)
|
| 2845 |
-
result = self.call_deepseek(
|
| 2846 |
-
system_prompt,
|
| 2847 |
-
marked_source,
|
| 2848 |
-
max_tokens=max(1800, min(9000, self._count_tokens(marked_source) * 2 + 1200)),
|
| 2849 |
-
temp=0.0,
|
| 2850 |
-
model_role='translation',
|
| 2851 |
-
thinking=False,
|
| 2852 |
-
allow_fallback=False,
|
| 2853 |
)
|
| 2854 |
-
|
| 2855 |
-
parsed = self._parse_marked_translation(result, [block_id for block_id, _ in batch])
|
| 2856 |
-
for block_id, source in batch:
|
| 2857 |
-
translated = parsed[block_id]
|
| 2858 |
-
self._validate_translation_block(source, translated, f'SP-P{block_id}', target_lang)
|
| 2859 |
-
translations[block_id] = translated
|
| 2860 |
|
| 2861 |
output_units = []
|
| 2862 |
body_index = 0
|
|
|
|
| 2074 |
|
| 2075 |
cfg = self.citation_semantic_audit
|
| 2076 |
enabled = bool(cfg.get("enabled", True))
|
|
|
|
| 2077 |
spans = self._citation_sentence_spans(text)
|
| 2078 |
if not spans:
|
| 2079 |
return text
|
|
|
|
| 2281 |
f"[{tag.upper()}]"
|
| 2282 |
for tag in re.findall(r"CITE-\d+", revised, flags=re.IGNORECASE)
|
| 2283 |
}
|
| 2284 |
+
if status in {"ok", "split"} and not revised_tags:
|
| 2285 |
+
raise DeepSeekRequestError(
|
| 2286 |
+
f"Citation semantic audit removed all citation tags for {record['id']} "
|
| 2287 |
+
f"while status={status}."
|
| 2288 |
+
)
|
| 2289 |
disallowed = revised_tags - set(record["allowed_tags"])
|
| 2290 |
if disallowed:
|
| 2291 |
raise DeepSeekRequestError(
|
|
|
|
| 2473 |
except PipelineCancelledError:
|
| 2474 |
raise
|
| 2475 |
except CitationAuditError as error:
|
| 2476 |
+
if bool(self.citation_semantic_audit.get('fail_closed', False)):
|
| 2477 |
+
raise
|
| 2478 |
logging.warning(
|
| 2479 |
'Citation semantic audit technical fallback: section=%s code=%s detail=%s',
|
| 2480 |
section_name,
|
|
|
|
| 2483 |
)
|
| 2484 |
value = self._local_citation_recovery(value, section_name)
|
| 2485 |
except Exception as error:
|
| 2486 |
+
if bool(self.citation_semantic_audit.get('fail_closed', False)):
|
| 2487 |
+
raise
|
| 2488 |
logging.warning(
|
| 2489 |
'Citation semantic audit fallback: section=%s error=%s',
|
| 2490 |
section_name,
|
|
|
|
| 2814 |
if not is_chinese and not re.search(r'[А-Яа-яЁёӘәҒғҚқҢңӨөҰұҮүҺһІі]', translated):
|
| 2815 |
raise DeepSeekRequestError(f'Translation block {block_id} contains no Cyrillic target-language text.')
|
| 2816 |
|
| 2817 |
+
def _translate_marked_batch_with_recovery(self, batch, system_prompt, target_lang):
|
| 2818 |
+
"""Translate one marked batch with a conservative marker-recovery path.
|
| 2819 |
+
|
| 2820 |
+
A multi-block response must preserve every marker exactly. If the model
|
| 2821 |
+
omits or reorders markers, retry each source block independently so no
|
| 2822 |
+
paragraph-to-translation mapping is guessed. For a single-source retry,
|
| 2823 |
+
a marker-free response can be bound safely to that sole source block,
|
| 2824 |
+
but it must still pass the existing deterministic translation guards.
|
| 2825 |
+
"""
|
| 2826 |
+
expected_ids = [block_id for block_id, _ in batch]
|
| 2827 |
+
marked_source = '\n\n'.join(
|
| 2828 |
+
f'[[SP-P{block_id}]]\n{source}' for block_id, source in batch
|
| 2829 |
+
)
|
| 2830 |
+
result = self.call_deepseek(
|
| 2831 |
+
system_prompt,
|
| 2832 |
+
marked_source,
|
| 2833 |
+
max_tokens=max(1800, min(9000, self._count_tokens(marked_source) * 2 + 1200)),
|
| 2834 |
+
temp=0.0,
|
| 2835 |
+
model_role='translation',
|
| 2836 |
+
thinking=False,
|
| 2837 |
+
allow_fallback=False,
|
| 2838 |
+
)
|
| 2839 |
+
result = self._remove_ai_meta_traces(result)
|
| 2840 |
+
|
| 2841 |
+
try:
|
| 2842 |
+
parsed = self._parse_marked_translation(result, expected_ids)
|
| 2843 |
+
except DeepSeekRequestError as error:
|
| 2844 |
+
if not str(error).startswith('Translation marker mismatch:'):
|
| 2845 |
+
raise
|
| 2846 |
+
|
| 2847 |
+
if len(batch) > 1:
|
| 2848 |
+
logging.warning(
|
| 2849 |
+
'Translation marker recovery: batch_size=%s expected=%s; retrying blocks independently.',
|
| 2850 |
+
len(batch),
|
| 2851 |
+
expected_ids,
|
| 2852 |
+
)
|
| 2853 |
+
recovered = {}
|
| 2854 |
+
for item in batch:
|
| 2855 |
+
recovered.update(
|
| 2856 |
+
self._translate_marked_batch_with_recovery(
|
| 2857 |
+
[item], system_prompt, target_lang
|
| 2858 |
+
)
|
| 2859 |
+
)
|
| 2860 |
+
return recovered
|
| 2861 |
+
|
| 2862 |
+
block_id, source = batch[0]
|
| 2863 |
+
unexpected_markers = re.findall(
|
| 2864 |
+
r'(?m)^\[\[SP-P(\d{6})\]\]\s*$',
|
| 2865 |
+
str(result or ''),
|
| 2866 |
+
)
|
| 2867 |
+
if unexpected_markers:
|
| 2868 |
+
raise DeepSeekRequestError(
|
| 2869 |
+
'Single-block translation returned an unexpected marker: '
|
| 2870 |
+
f'expected={block_id}, got={unexpected_markers}.'
|
| 2871 |
+
) from error
|
| 2872 |
+
|
| 2873 |
+
translated = str(result or '').strip()
|
| 2874 |
+
if not translated:
|
| 2875 |
+
raise DeepSeekRequestError(
|
| 2876 |
+
f'Single-block translation recovery returned empty text for SP-P{block_id}.'
|
| 2877 |
+
) from error
|
| 2878 |
+
self._validate_translation_block(
|
| 2879 |
+
source, translated, f'SP-P{block_id}', target_lang
|
| 2880 |
+
)
|
| 2881 |
+
logging.info(
|
| 2882 |
+
'Translation marker recovery accepted marker-free single block: SP-P%s',
|
| 2883 |
+
block_id,
|
| 2884 |
+
)
|
| 2885 |
+
return {block_id: translated}
|
| 2886 |
+
|
| 2887 |
+
for block_id, source in batch:
|
| 2888 |
+
translated = parsed[block_id]
|
| 2889 |
+
self._validate_translation_block(
|
| 2890 |
+
source, translated, f'SP-P{block_id}', target_lang
|
| 2891 |
+
)
|
| 2892 |
+
return parsed
|
| 2893 |
+
|
| 2894 |
def translate_locked_manuscript(self, manuscript, target_lang):
|
| 2895 |
value = str(manuscript or '').strip()
|
| 2896 |
if not value:
|
|
|
|
| 2924 |
system_prompt = self._translation_prompt(target_lang) + '\nUse this terminology glossary consistently:\n' + json.dumps(glossary, ensure_ascii=False)
|
| 2925 |
for batch in batches:
|
| 2926 |
self._check_cancelled()
|
| 2927 |
+
parsed = self._translate_marked_batch_with_recovery(
|
| 2928 |
+
batch, system_prompt, target_lang
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2929 |
)
|
| 2930 |
+
translations.update(parsed)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2931 |
|
| 2932 |
output_units = []
|
| 2933 |
body_index = 0
|
smartpaper_restore_bridge/__pycache__/__init__.cpython-313.pyc
CHANGED
|
Binary files a/smartpaper_restore_bridge/__pycache__/__init__.cpython-313.pyc and b/smartpaper_restore_bridge/__pycache__/__init__.cpython-313.pyc differ
|
|
|
utils/__pycache__/docx_builder.cpython-313.pyc
CHANGED
|
Binary files a/utils/__pycache__/docx_builder.cpython-313.pyc and b/utils/__pycache__/docx_builder.cpython-313.pyc differ
|
|
|
utils/__pycache__/file_parser.cpython-313.pyc
CHANGED
|
Binary files a/utils/__pycache__/file_parser.cpython-313.pyc and b/utils/__pycache__/file_parser.cpython-313.pyc differ
|
|
|