Spaces:
Running
Running
A5-R3-R7-R2C-R3 adaptive finalization recovery scoped deployment
#119
by GuXiaopeng - opened
- core/generation_runner.py +39 -2
- core/pipeline_agent.py +55 -10
core/generation_runner.py
CHANGED
|
@@ -5229,7 +5229,25 @@ The previous attempt for this same manuscript unit was rejected by validation. R
|
|
| 5229 |
agent.enforce_resolved_claim_specific_citations(body_only)
|
| 5230 |
post_front_ledger = agent.finalize_resolved_citation_ledger(body_only)
|
| 5231 |
if set(post_front_ledger) != set(final_citation_ledger):
|
| 5232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5233 |
agent.validate_final_citation_bijection(body_only, _reference_tail, final_citation_ledger)
|
| 5234 |
self._upsert_quality_diagnostic(
|
| 5235 |
quality_diagnostics, self._validate_target_length(full_draft_en, target_words)
|
|
@@ -5451,12 +5469,31 @@ The previous attempt for this same manuscript unit was rejected by validation. R
|
|
| 5451 |
)
|
| 5452 |
try:
|
| 5453 |
agent.validate_translation_alignment(full_draft_en, full_draft_translated)
|
| 5454 |
-
agent.validate_reference_preservation(full_draft_en, full_draft_translated, target_lang)
|
| 5455 |
except Exception as exc:
|
| 5456 |
self._record_adaptive_completion_residual(
|
| 5457 |
quality_notes, [{'type':'translation_final_alignment_review','reason':str(exc)[:500]}],
|
| 5458 |
section_name='Target-language manuscript', stage='translation_final_review'
|
| 5459 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5460 |
self._publish_target_preview(
|
| 5461 |
full_draft_translated, 'translation_complete', force=True, complete=True
|
| 5462 |
)
|
|
|
|
| 5229 |
agent.enforce_resolved_claim_specific_citations(body_only)
|
| 5230 |
post_front_ledger = agent.finalize_resolved_citation_ledger(body_only)
|
| 5231 |
if set(post_front_ledger) != set(final_citation_ledger):
|
| 5232 |
+
logging.warning(
|
| 5233 |
+
"Adaptive finalization resealed citation ledger after Final Front Matter | before=%s after=%s",
|
| 5234 |
+
len(final_citation_ledger), len(post_front_ledger),
|
| 5235 |
+
)
|
| 5236 |
+
# Final Front Matter is allowed to add/remove an already-authoritative
|
| 5237 |
+
# citation. Rebuild References deterministically from the post-front
|
| 5238 |
+
# resolved body instead of discarding an otherwise complete manuscript.
|
| 5239 |
+
# Orphan/ambiguous citations still fail closed inside ledger finalization.
|
| 5240 |
+
final_citation_ledger = list(post_front_ledger)
|
| 5241 |
+
references = agent.build_final_reference_lines(final_citation_ledger)
|
| 5242 |
+
final_reference_count = len(references)
|
| 5243 |
+
full_draft_en = body_only.rstrip() + "\n\n# References\n" + "\n\n".join(references)
|
| 5244 |
+
coverage_summary = dict(state.get("evidence_coverage_summary") or {})
|
| 5245 |
+
if coverage_summary:
|
| 5246 |
+
coverage_summary["final_reference_count"] = final_reference_count
|
| 5247 |
+
state["evidence_coverage_summary"] = coverage_summary
|
| 5248 |
+
body_only, _reference_tail = agent.validate_reference_tail(
|
| 5249 |
+
full_draft_en, minimum_references=final_reference_count
|
| 5250 |
+
)
|
| 5251 |
agent.validate_final_citation_bijection(body_only, _reference_tail, final_citation_ledger)
|
| 5252 |
self._upsert_quality_diagnostic(
|
| 5253 |
quality_diagnostics, self._validate_target_length(full_draft_en, target_words)
|
|
|
|
| 5469 |
)
|
| 5470 |
try:
|
| 5471 |
agent.validate_translation_alignment(full_draft_en, full_draft_translated)
|
|
|
|
| 5472 |
except Exception as exc:
|
| 5473 |
self._record_adaptive_completion_residual(
|
| 5474 |
quality_notes, [{'type':'translation_final_alignment_review','reason':str(exc)[:500]}],
|
| 5475 |
section_name='Target-language manuscript', stage='translation_final_review'
|
| 5476 |
)
|
| 5477 |
+
try:
|
| 5478 |
+
agent.validate_reference_preservation(full_draft_en, full_draft_translated, target_lang)
|
| 5479 |
+
except Exception as exc:
|
| 5480 |
+
logging.warning(
|
| 5481 |
+
"Adaptive target References rebind | language=%s reason=%s",
|
| 5482 |
+
target_lang, str(exc)[:300],
|
| 5483 |
+
)
|
| 5484 |
+
full_draft_translated = agent.rebind_target_reference_tail(
|
| 5485 |
+
full_draft_translated, reference_tail, target_lang
|
| 5486 |
+
)
|
| 5487 |
+
# References are identity-bearing academic data. Recovery is
|
| 5488 |
+
# deterministic and provider-free; if this still fails, package
|
| 5489 |
+
# generation must stop rather than ship a corrupted bibliography.
|
| 5490 |
+
agent.validate_reference_preservation(
|
| 5491 |
+
full_draft_en, full_draft_translated, target_lang
|
| 5492 |
+
)
|
| 5493 |
+
self._record_adaptive_completion_residual(
|
| 5494 |
+
quality_notes, [{'type':'target_reference_tail_rebound','reason':str(exc)[:500]}],
|
| 5495 |
+
section_name='Target-language manuscript', stage='translation_final_recovery'
|
| 5496 |
+
)
|
| 5497 |
self._publish_target_preview(
|
| 5498 |
full_draft_translated, 'translation_complete', force=True, complete=True
|
| 5499 |
)
|
core/pipeline_agent.py
CHANGED
|
@@ -2659,15 +2659,52 @@ If the immutable hypothesis contract is non-empty, preserve its H-identifiers an
|
|
| 2659 |
)
|
| 2660 |
return sorted(ledger, key=self._citation_tag_number)
|
| 2661 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2662 |
def build_final_reference_lines(self, ledger_tags):
|
| 2663 |
-
"""Build one English reference
|
| 2664 |
references = {}
|
| 2665 |
for tag in ledger_tags or []:
|
| 2666 |
metadata = self.citation_map.get(str(tag)) or {}
|
| 2667 |
-
ref =
|
| 2668 |
if not ref:
|
| 2669 |
raise DeepSeekRequestError(f'Final citation ledger metadata missing reference text: {tag}')
|
| 2670 |
-
references.setdefault(
|
| 2671 |
# Reader-facing References follow the conventional alphabetical order.
|
| 2672 |
return [f"- {ref}" for ref in sorted(references.values(), key=lambda value: value.casefold())]
|
| 2673 |
|
|
@@ -2680,18 +2717,14 @@ If the immutable hypothesis contract is non-empty, preserve its H-identifiers an
|
|
| 2680 |
for tag in ledger:
|
| 2681 |
metadata = self.citation_map.get(str(tag)) or {}
|
| 2682 |
marker = str(metadata.get('in_text') or '').strip()
|
| 2683 |
-
ref =
|
| 2684 |
if not marker or not ref:
|
| 2685 |
raise DeepSeekRequestError(f'Final citation ledger is incomplete: {tag}')
|
| 2686 |
if marker not in body:
|
| 2687 |
raise DeepSeekRequestError(f'Final citation ledger contains a citation absent from body: {tag}')
|
| 2688 |
-
expected.append(
|
| 2689 |
|
| 2690 |
-
actual =
|
| 2691 |
-
re.sub(r'\s+', ' ', line[2:]).strip()
|
| 2692 |
-
for line in tail.splitlines()
|
| 2693 |
-
if line.strip().startswith('- ')
|
| 2694 |
-
]
|
| 2695 |
if len(actual) != len(set(actual)):
|
| 2696 |
raise DeepSeekRequestError('Final References contains duplicate entries.')
|
| 2697 |
if set(actual) != set(expected):
|
|
@@ -2708,6 +2741,18 @@ If the immutable hypothesis contract is non-empty, preserve its H-identifiers an
|
|
| 2708 |
)
|
| 2709 |
return True
|
| 2710 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2711 |
@classmethod
|
| 2712 |
def validate_reference_preservation(cls, english_text, translated_text, target_lang):
|
| 2713 |
"""References are immutable English data in ZH/RU/KK deliverables."""
|
|
|
|
| 2659 |
)
|
| 2660 |
return sorted(ledger, key=self._citation_tag_number)
|
| 2661 |
|
| 2662 |
+
@staticmethod
|
| 2663 |
+
def _canonical_reference_text(value):
|
| 2664 |
+
"""Canonicalize formatting-only reference variance without changing identity.
|
| 2665 |
+
|
| 2666 |
+
Provider metadata can legally contain embedded CR/LF, tabs, Unicode line
|
| 2667 |
+
separators, non-breaking spaces, or zero-width formatting characters. A
|
| 2668 |
+
reference is one bibliographic object, so those transport/layout artifacts
|
| 2669 |
+
must never turn one source into multiple Markdown lines at finalization.
|
| 2670 |
+
"""
|
| 2671 |
+
text = str(value or '')
|
| 2672 |
+
text = text.replace('\r\n', '\n').replace('\r', '\n')
|
| 2673 |
+
text = re.sub(r'[\u200b\u2060\ufeff]+', '', text)
|
| 2674 |
+
return re.sub(r'\s+', ' ', text).strip()
|
| 2675 |
+
|
| 2676 |
+
@classmethod
|
| 2677 |
+
def _canonical_reference_entries_from_tail(cls, reference_tail):
|
| 2678 |
+
"""Parse bullet references while deterministically joining wrapped lines."""
|
| 2679 |
+
entries = []
|
| 2680 |
+
current = []
|
| 2681 |
+
for raw_line in str(reference_tail or '').replace('\r\n', '\n').replace('\r', '\n').split('\n'):
|
| 2682 |
+
line = raw_line.strip()
|
| 2683 |
+
if not line:
|
| 2684 |
+
continue
|
| 2685 |
+
if line.startswith('- '):
|
| 2686 |
+
if current:
|
| 2687 |
+
entries.append(cls._canonical_reference_text(' '.join(current)))
|
| 2688 |
+
current = [line[2:].strip()]
|
| 2689 |
+
elif current:
|
| 2690 |
+
current.append(line)
|
| 2691 |
+
else:
|
| 2692 |
+
raise DeepSeekRequestError(
|
| 2693 |
+
'Final References contains non-entry text before the first reference bullet.'
|
| 2694 |
+
)
|
| 2695 |
+
if current:
|
| 2696 |
+
entries.append(cls._canonical_reference_text(' '.join(current)))
|
| 2697 |
+
return entries
|
| 2698 |
+
|
| 2699 |
def build_final_reference_lines(self, ledger_tags):
|
| 2700 |
+
"""Build one canonical single-line English reference per ledger source."""
|
| 2701 |
references = {}
|
| 2702 |
for tag in ledger_tags or []:
|
| 2703 |
metadata = self.citation_map.get(str(tag)) or {}
|
| 2704 |
+
ref = self._canonical_reference_text(metadata.get('ref') or '')
|
| 2705 |
if not ref:
|
| 2706 |
raise DeepSeekRequestError(f'Final citation ledger metadata missing reference text: {tag}')
|
| 2707 |
+
references.setdefault(ref.casefold(), ref)
|
| 2708 |
# Reader-facing References follow the conventional alphabetical order.
|
| 2709 |
return [f"- {ref}" for ref in sorted(references.values(), key=lambda value: value.casefold())]
|
| 2710 |
|
|
|
|
| 2717 |
for tag in ledger:
|
| 2718 |
metadata = self.citation_map.get(str(tag)) or {}
|
| 2719 |
marker = str(metadata.get('in_text') or '').strip()
|
| 2720 |
+
ref = self._canonical_reference_text(metadata.get('ref') or '')
|
| 2721 |
if not marker or not ref:
|
| 2722 |
raise DeepSeekRequestError(f'Final citation ledger is incomplete: {tag}')
|
| 2723 |
if marker not in body:
|
| 2724 |
raise DeepSeekRequestError(f'Final citation ledger contains a citation absent from body: {tag}')
|
| 2725 |
+
expected.append(ref)
|
| 2726 |
|
| 2727 |
+
actual = self._canonical_reference_entries_from_tail(tail)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2728 |
if len(actual) != len(set(actual)):
|
| 2729 |
raise DeepSeekRequestError('Final References contains duplicate entries.')
|
| 2730 |
if set(actual) != set(expected):
|
|
|
|
| 2741 |
)
|
| 2742 |
return True
|
| 2743 |
|
| 2744 |
+
@classmethod
|
| 2745 |
+
def rebind_target_reference_tail(cls, translated_text, frozen_reference_tail, target_lang):
|
| 2746 |
+
"""Replace a target References tail from frozen English bytes, without LLM use."""
|
| 2747 |
+
target = str(translated_text or '').strip()
|
| 2748 |
+
heading = cls._localized_reference_heading(target_lang)
|
| 2749 |
+
matches = list(re.finditer(rf'(?m)^#\s+{re.escape(heading)}\s*$', target))
|
| 2750 |
+
body = target[:matches[0].start()].rstrip() if matches else target.rstrip()
|
| 2751 |
+
tail = str(frozen_reference_tail or '').strip()
|
| 2752 |
+
if not tail:
|
| 2753 |
+
return body + f"\n\n# {heading}\n"
|
| 2754 |
+
return body + f"\n\n# {heading}\n" + tail
|
| 2755 |
+
|
| 2756 |
@classmethod
|
| 2757 |
def validate_reference_preservation(cls, english_text, translated_text, target_lang):
|
| 2758 |
"""References are immutable English data in ZH/RU/KK deliverables."""
|