Spaces:
Running
Running
S0-R1-C5.5-R6-R7 Canonical Glossary Protected-IR Compiler Closure
#95
by GuXiaopeng - opened
- core/pipeline_agent.py +341 -85
core/pipeline_agent.py
CHANGED
|
@@ -74,7 +74,9 @@ class SSciPipelineAgent:
|
|
| 74 |
MAX_TRANSLATION_CLAIM_REPAIR_ATTEMPTS = 2
|
| 75 |
MAX_TRANSLATION_CLAIM_TOTAL_REPAIRS = 6
|
| 76 |
PROTECTED_ENTITY_LEDGER_VERSION = 'protected-entity-ledger-v2'
|
| 77 |
-
CANONICAL_GLOSSARY_QUALITY_VERSION = 'canonical-glossary-quality-
|
|
|
|
|
|
|
| 78 |
SEMANTIC_CLAIM_FRAME_VERSION = 'smartpaper-core-semantic-claim-frame-v1'
|
| 79 |
CITATION_CLAIM_FRAME_VERSION = 'smartpaper-citation-claim-frame-v1'
|
| 80 |
|
|
@@ -7163,8 +7165,224 @@ TEXT:
|
|
| 7163 |
raise DeepSeekRequestError(f'Translation left unresolved protected placeholders in {block_id}.')
|
| 7164 |
return text.strip() if keep_claim_markers else cls._strip_translation_claim_markers(text)
|
| 7165 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7166 |
def seed_canonical_concept_glossary(self, concept_ledger, target_lang, existing=None):
|
| 7167 |
-
"""Translate/freeze canonical task concepts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7168 |
mapping = dict(existing or {})
|
| 7169 |
rows = (dict(concept_ledger or {}).get('items') or []) if isinstance(concept_ledger, dict) else []
|
| 7170 |
sources = []
|
|
@@ -7184,45 +7402,52 @@ TEXT:
|
|
| 7184 |
flexible_sources.append(term)
|
| 7185 |
else:
|
| 7186 |
strict_sources.append(term)
|
| 7187 |
-
# Only true research constructs receive deterministic canonical-identity
|
| 7188 |
-
# enforcement. Descriptive academic terms still receive a preferred
|
| 7189 |
-
# glossary translation but may vary naturally with target-language
|
| 7190 |
-
# grammar or equivalent wording; semantic QA remains authoritative.
|
| 7191 |
self._canonical_concept_sources = set(strict_sources)
|
| 7192 |
self._canonical_flexible_sources = set(flexible_sources)
|
| 7193 |
self._canonical_concept_kinds = dict(source_kinds)
|
| 7194 |
if not sources:
|
| 7195 |
return mapping
|
| 7196 |
|
| 7197 |
-
|
| 7198 |
-
#
|
| 7199 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7200 |
translate_terms = []
|
| 7201 |
-
for term in
|
| 7202 |
compact = re.sub(r'[^A-Za-z0-9]', '', term)
|
| 7203 |
-
technical = bool(compact) and (
|
|
|
|
|
|
|
|
|
|
| 7204 |
if technical:
|
| 7205 |
mapping[term] = term
|
| 7206 |
else:
|
| 7207 |
translate_terms.append(term)
|
| 7208 |
if translate_terms:
|
| 7209 |
-
|
| 7210 |
-
response = self.call_deepseek(
|
| 7211 |
-
f"Translate the supplied frozen research concepts into {target_lang}. {rule} Each source is a canonical concept identity, not a stylistic phrase. Give one stable canonical target term for each source; do not merge distinct sources or replace one concept with a near-synonym. Return JSON only as {{\"terms\":[{{\"source\":\"...\",\"target\":\"...\"}}]}} and return every source exactly once.",
|
| 7212 |
-
json.dumps({'terms': translate_terms}, ensure_ascii=False),
|
| 7213 |
-
max_tokens=max(1200, min(5000, 120 + len(translate_terms) * 90)),
|
| 7214 |
-
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 7215 |
-
)
|
| 7216 |
-
parsed = self._extract_json_from_text(response)
|
| 7217 |
-
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
| 7218 |
-
returned = {
|
| 7219 |
-
re.sub(r'\s+', ' ', str(item.get('source') or '')).strip(): re.sub(r'\s+', ' ', str(item.get('target') or '')).strip()
|
| 7220 |
-
for item in items if isinstance(item, dict)
|
| 7221 |
-
}
|
| 7222 |
-
if set(returned) != set(translate_terms) or any(not returned.get(term) for term in translate_terms):
|
| 7223 |
-
raise DeepSeekRequestError('Canonical concept glossary identity mismatch or omitted term.')
|
| 7224 |
for term in translate_terms:
|
| 7225 |
-
target =
|
|
|
|
|
|
|
| 7226 |
if target.casefold() == term.casefold() and re.search(r'[a-z]{4,}', term):
|
| 7227 |
raise DeepSeekRequestError(f'Canonical concept remained untranslated: {term[:80]}')
|
| 7228 |
mapping[term] = target
|
|
@@ -7287,28 +7512,15 @@ TEXT:
|
|
| 7287 |
|
| 7288 |
if cls._canonical_target_surface_suspicious(target_value, target_lang):
|
| 7289 |
defects.append('MALFORMED_SURFACE')
|
| 7290 |
-
|
| 7291 |
-
defects.append('PROTECTED_ANCHOR_LOSS')
|
| 7292 |
return list(dict.fromkeys(defects))
|
| 7293 |
|
| 7294 |
def validate_and_refine_canonical_concept_glossary(self, concept_ledger, target_lang, glossary):
|
| 7295 |
-
"""Review strict canonical terms
|
| 7296 |
-
|
| 7297 |
-
|
| 7298 |
-
|
| 7299 |
-
|
| 7300 |
-
well-formed surface, protected-anchor preservation).
|
| 7301 |
-
2. The independent model reviewer may replace a candidate only with one
|
| 7302 |
-
distinct, deterministic-safe correction.
|
| 7303 |
-
3. If the reviewer rejects/omits/contradicts a candidate but supplies no
|
| 7304 |
-
usable correction, an already safe candidate is retained with an
|
| 7305 |
-
assurance warning instead of aborting the whole manuscript.
|
| 7306 |
-
4. If the candidate itself is deterministic-unsafe and no safe correction
|
| 7307 |
-
exists, the gate remains fail-closed.
|
| 7308 |
-
|
| 7309 |
-
This prevents a secondary quality-review formatting/judgment failure from
|
| 7310 |
-
becoming a single point of failure across Chinese, Russian, and Kazakh
|
| 7311 |
-
translation while preserving genuine terminology-integrity barriers.
|
| 7312 |
"""
|
| 7313 |
mapping = dict(glossary or {})
|
| 7314 |
rows = (dict(concept_ledger or {}).get('items') or []) if isinstance(concept_ledger, dict) else []
|
|
@@ -7324,16 +7536,47 @@ TEXT:
|
|
| 7324 |
if not strict_sources:
|
| 7325 |
return mapping
|
| 7326 |
|
| 7327 |
-
|
| 7328 |
-
|
| 7329 |
-
|
| 7330 |
-
]
|
| 7331 |
-
candidate_defects = {
|
| 7332 |
-
|
| 7333 |
-
|
| 7334 |
-
|
| 7335 |
-
|
| 7336 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7337 |
empty_sources = [source for source, defects in candidate_defects.items() if 'EMPTY_TARGET' in defects]
|
| 7338 |
if empty_sources:
|
| 7339 |
raise DeepSeekRequestError(
|
|
@@ -7344,16 +7587,16 @@ TEXT:
|
|
| 7344 |
try:
|
| 7345 |
response = self.call_deepseek(
|
| 7346 |
(
|
| 7347 |
-
f
|
| 7348 |
-
|
| 7349 |
-
|
| 7350 |
-
|
| 7351 |
-
|
| 7352 |
-
|
| 7353 |
-
'
|
| 7354 |
-
" and return every
|
| 7355 |
),
|
| 7356 |
-
json.dumps({'terms': payload}, ensure_ascii=False),
|
| 7357 |
max_tokens=max(1200, min(4200, 180 + len(payload) * 120)),
|
| 7358 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 7359 |
)
|
|
@@ -7377,12 +7620,12 @@ TEXT:
|
|
| 7377 |
for item in items:
|
| 7378 |
if not isinstance(item, dict):
|
| 7379 |
continue
|
| 7380 |
-
|
| 7381 |
-
if
|
| 7382 |
-
returned[
|
| 7383 |
extra_count = sum(
|
| 7384 |
1 for item in items if isinstance(item, dict)
|
| 7385 |
-
and
|
| 7386 |
)
|
| 7387 |
if extra_count:
|
| 7388 |
self._record_translation_assurance(
|
|
@@ -7392,9 +7635,19 @@ TEXT:
|
|
| 7392 |
|
| 7393 |
refined = dict(mapping)
|
| 7394 |
for source in strict_sources:
|
| 7395 |
-
|
|
|
|
| 7396 |
defects = list(candidate_defects.get(source) or [])
|
| 7397 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7398 |
if not item:
|
| 7399 |
if defects:
|
| 7400 |
raise DeepSeekRequestError(
|
|
@@ -7409,16 +7662,22 @@ TEXT:
|
|
| 7409 |
|
| 7410 |
accepted_raw = item.get('accepted')
|
| 7411 |
accepted = accepted_raw is True or str(accepted_raw).strip().casefold() == 'true'
|
| 7412 |
-
|
| 7413 |
-
|
| 7414 |
-
|
| 7415 |
-
|
| 7416 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7417 |
|
| 7418 |
-
|
| 7419 |
-
# accept but also changes the term, keep a safe frozen candidate; if
|
| 7420 |
-
# the candidate is unsafe, a distinct safe target may still rescue it.
|
| 7421 |
-
if accepted and reviewed_target in {'', candidate}:
|
| 7422 |
if not defects:
|
| 7423 |
continue
|
| 7424 |
raise DeepSeekRequestError(
|
|
@@ -7437,13 +7696,10 @@ TEXT:
|
|
| 7437 |
continue
|
| 7438 |
|
| 7439 |
if not defects:
|
| 7440 |
-
# Reviewer vetoes, omits a correction, proposes an invalid
|
| 7441 |
-
# correction, or contradicts accepted=true. None of these is
|
| 7442 |
-
# actionable evidence against an already deterministic-safe term.
|
| 7443 |
reason = 'NON_ACTIONABLE_REJECTION'
|
| 7444 |
-
if accepted and
|
| 7445 |
reason = 'ACCEPTED_BUT_CHANGED'
|
| 7446 |
-
elif
|
| 7447 |
reason = 'INVALID_CORRECTION'
|
| 7448 |
self._record_translation_assurance(
|
| 7449 |
'CANONICAL_GLOSSARY_REVIEW_INCONCLUSIVE', target_lang=target_lang, severity='diagnostic',
|
|
|
|
| 74 |
MAX_TRANSLATION_CLAIM_REPAIR_ATTEMPTS = 2
|
| 75 |
MAX_TRANSLATION_CLAIM_TOTAL_REPAIRS = 6
|
| 76 |
PROTECTED_ENTITY_LEDGER_VERSION = 'protected-entity-ledger-v2'
|
| 77 |
+
CANONICAL_GLOSSARY_QUALITY_VERSION = 'canonical-glossary-quality-v4'
|
| 78 |
+
CANONICAL_GLOSSARY_IR_VERSION = 'canonical-glossary-protected-ir-v1'
|
| 79 |
+
MAX_CANONICAL_GLOSSARY_IR_ATTEMPTS = 2
|
| 80 |
SEMANTIC_CLAIM_FRAME_VERSION = 'smartpaper-core-semantic-claim-frame-v1'
|
| 81 |
CITATION_CLAIM_FRAME_VERSION = 'smartpaper-citation-claim-frame-v1'
|
| 82 |
|
|
|
|
| 7165 |
raise DeepSeekRequestError(f'Translation left unresolved protected placeholders in {block_id}.')
|
| 7166 |
return text.strip() if keep_claim_markers else cls._strip_translation_claim_markers(text)
|
| 7167 |
|
| 7168 |
+
@classmethod
|
| 7169 |
+
def _compile_canonical_glossary_ir(cls, source):
|
| 7170 |
+
"""Compile one canonical concept into a protected, server-owned IR.
|
| 7171 |
+
|
| 7172 |
+
Exact technical identities are replaced by opaque SP-G placeholders before
|
| 7173 |
+
any model sees the concept. The model may translate only the linguistic
|
| 7174 |
+
surface around those placeholders; exact anchor values remain server-side.
|
| 7175 |
+
"""
|
| 7176 |
+
source_value = re.sub(r'\s+', ' ', str(source or '')).strip()
|
| 7177 |
+
mapping = {}
|
| 7178 |
+
counter = 0
|
| 7179 |
+
pattern = cls._translation_protected_pattern()
|
| 7180 |
+
|
| 7181 |
+
def replace(match):
|
| 7182 |
+
nonlocal counter
|
| 7183 |
+
original = match.group(0)
|
| 7184 |
+
anchor_type = cls._translation_anchor_type(original)
|
| 7185 |
+
if cls._translation_fact_render_policy(anchor_type) != 'exact':
|
| 7186 |
+
return original
|
| 7187 |
+
counter += 1
|
| 7188 |
+
placeholder = f'[[SP-G{counter:06d}]]'
|
| 7189 |
+
mapping[placeholder] = {
|
| 7190 |
+
'source': original,
|
| 7191 |
+
'type': anchor_type,
|
| 7192 |
+
'render_policy': 'exact',
|
| 7193 |
+
}
|
| 7194 |
+
return placeholder
|
| 7195 |
+
|
| 7196 |
+
protected_source = pattern.sub(replace, source_value)
|
| 7197 |
+
return {
|
| 7198 |
+
'ir_version': cls.CANONICAL_GLOSSARY_IR_VERSION,
|
| 7199 |
+
'source': source_value,
|
| 7200 |
+
'protected_source': protected_source,
|
| 7201 |
+
'anchors': mapping,
|
| 7202 |
+
}
|
| 7203 |
+
|
| 7204 |
+
@staticmethod
|
| 7205 |
+
def _canonical_glossary_ir_placeholders(value):
|
| 7206 |
+
return re.findall(r'\[\[SP-G\d{6}\]\]', str(value or ''))
|
| 7207 |
+
|
| 7208 |
+
@classmethod
|
| 7209 |
+
def _validate_canonical_glossary_ir_target(cls, protected_target, ir, block_id=''):
|
| 7210 |
+
"""Fail closed on deleted, duplicated, invented, or reordered SP-G anchors."""
|
| 7211 |
+
text = re.sub(r'\s+', ' ', str(protected_target or '')).strip()
|
| 7212 |
+
expected = list(dict(ir or {}).get('anchors', {}))
|
| 7213 |
+
found = cls._canonical_glossary_ir_placeholders(text)
|
| 7214 |
+
if Counter(found) != Counter(expected):
|
| 7215 |
+
missing = list((Counter(expected) - Counter(found)).elements())[:8]
|
| 7216 |
+
added = list((Counter(found) - Counter(expected)).elements())[:8]
|
| 7217 |
+
raise DeepSeekRequestError(
|
| 7218 |
+
f'Canonical glossary protected IR placeholder mismatch in {block_id}: '
|
| 7219 |
+
f'missing={missing}, added={added}.'
|
| 7220 |
+
)
|
| 7221 |
+
if found != expected:
|
| 7222 |
+
raise DeepSeekRequestError(
|
| 7223 |
+
f'Canonical glossary protected IR anchor order changed in {block_id}: '
|
| 7224 |
+
f'expected={expected}, got={found}.'
|
| 7225 |
+
)
|
| 7226 |
+
return True
|
| 7227 |
+
|
| 7228 |
+
@classmethod
|
| 7229 |
+
def _restore_canonical_glossary_ir_target(cls, protected_target, ir, block_id=''):
|
| 7230 |
+
cls._validate_canonical_glossary_ir_target(protected_target, ir, block_id=block_id)
|
| 7231 |
+
text = re.sub(r'\s+', ' ', str(protected_target or '')).strip()
|
| 7232 |
+
for placeholder, manifest in dict(dict(ir or {}).get('anchors', {})).items():
|
| 7233 |
+
text = text.replace(placeholder, str(manifest.get('source') or ''))
|
| 7234 |
+
if re.search(r'\[\[SP-G\d{6}\]\]', text):
|
| 7235 |
+
raise DeepSeekRequestError(
|
| 7236 |
+
f'Canonical glossary protected IR left unresolved placeholders in {block_id}.'
|
| 7237 |
+
)
|
| 7238 |
+
return text.strip()
|
| 7239 |
+
|
| 7240 |
+
@classmethod
|
| 7241 |
+
def _protect_existing_canonical_target_with_ir(cls, target, ir, block_id=''):
|
| 7242 |
+
"""Project a sealed target back into SP-G form for secondary review.
|
| 7243 |
+
|
| 7244 |
+
This is used only after deterministic anchor checks pass. Reviewers never
|
| 7245 |
+
receive the raw exact anchor values and therefore cannot translate or rename
|
| 7246 |
+
them; they may only edit the surrounding linguistic surface.
|
| 7247 |
+
"""
|
| 7248 |
+
text = re.sub(r'\s+', ' ', str(target or '')).strip()
|
| 7249 |
+
for placeholder, manifest in dict(dict(ir or {}).get('anchors', {})).items():
|
| 7250 |
+
surface = str(manifest.get('source') or '')
|
| 7251 |
+
if not surface:
|
| 7252 |
+
continue
|
| 7253 |
+
pos = text.find(surface)
|
| 7254 |
+
if pos < 0:
|
| 7255 |
+
raise DeepSeekRequestError(
|
| 7256 |
+
f'Canonical glossary target cannot be projected to protected IR in {block_id}: '
|
| 7257 |
+
f'missing exact anchor type={manifest.get("type") or "IMMUTABLE"}.'
|
| 7258 |
+
)
|
| 7259 |
+
text = text[:pos] + placeholder + text[pos + len(surface):]
|
| 7260 |
+
cls._validate_canonical_glossary_ir_target(text, ir, block_id=block_id)
|
| 7261 |
+
return text
|
| 7262 |
+
|
| 7263 |
+
@classmethod
|
| 7264 |
+
def _canonical_exact_anchor_sequence(cls, value):
|
| 7265 |
+
"""Return exact-render technical anchors as a byte-stable sequence."""
|
| 7266 |
+
_protected, mapping = cls._protect_translation_tokens(str(value or ''))
|
| 7267 |
+
return [
|
| 7268 |
+
str(manifest.get('source') or '')
|
| 7269 |
+
for manifest in dict(mapping or {}).values()
|
| 7270 |
+
if isinstance(manifest, dict)
|
| 7271 |
+
and str(manifest.get('render_policy') or '') == 'exact'
|
| 7272 |
+
and str(manifest.get('source') or '') not in {'', '|'}
|
| 7273 |
+
]
|
| 7274 |
+
|
| 7275 |
+
@classmethod
|
| 7276 |
+
def _canonical_anchor_drift_defects(cls, source, target):
|
| 7277 |
+
expected = cls._canonical_exact_anchor_sequence(source)
|
| 7278 |
+
actual = cls._canonical_exact_anchor_sequence(target)
|
| 7279 |
+
expected_counts = Counter(expected)
|
| 7280 |
+
actual_counts = Counter(actual)
|
| 7281 |
+
defects = []
|
| 7282 |
+
if any(actual_counts.get(anchor, 0) < count for anchor, count in expected_counts.items()):
|
| 7283 |
+
defects.append('PROTECTED_ANCHOR_LOSS')
|
| 7284 |
+
if any(actual_counts.get(anchor, 0) > count for anchor, count in expected_counts.items()):
|
| 7285 |
+
defects.append('PROTECTED_ANCHOR_DUPLICATION')
|
| 7286 |
+
# Exact target-side technical identities that were not source-owned are
|
| 7287 |
+
# not allowed to appear inside a strict canonical identity.
|
| 7288 |
+
if any(anchor not in expected_counts for anchor in actual_counts):
|
| 7289 |
+
defects.append('PROTECTED_ANCHOR_ADDITION')
|
| 7290 |
+
if expected_counts == actual_counts and expected != actual:
|
| 7291 |
+
defects.append('PROTECTED_ANCHOR_ORDER_DRIFT')
|
| 7292 |
+
return defects
|
| 7293 |
+
|
| 7294 |
+
def _translate_canonical_glossary_ir_terms(self, terms, target_lang):
|
| 7295 |
+
"""Translate canonical concepts through protected IR with one bounded skeleton retry."""
|
| 7296 |
+
terms = list(terms or [])
|
| 7297 |
+
if not terms:
|
| 7298 |
+
return {}
|
| 7299 |
+
ir_by_id = {}
|
| 7300 |
+
source_by_id = {}
|
| 7301 |
+
for index, source in enumerate(terms, start=1):
|
| 7302 |
+
term_id = f'G{index:06d}'
|
| 7303 |
+
ir_by_id[term_id] = self._compile_canonical_glossary_ir(source)
|
| 7304 |
+
source_by_id[term_id] = source
|
| 7305 |
+
|
| 7306 |
+
resolved = {}
|
| 7307 |
+
pending = list(ir_by_id)
|
| 7308 |
+
rule = self._translation_language_rule(target_lang)
|
| 7309 |
+
for attempt in range(1, self.MAX_CANONICAL_GLOSSARY_IR_ATTEMPTS + 1):
|
| 7310 |
+
if not pending:
|
| 7311 |
+
break
|
| 7312 |
+
payload = []
|
| 7313 |
+
for term_id in pending:
|
| 7314 |
+
ir = ir_by_id[term_id]
|
| 7315 |
+
payload.append({
|
| 7316 |
+
'term_id': term_id,
|
| 7317 |
+
'protected_source': ir['protected_source'],
|
| 7318 |
+
'anchor_manifest': [
|
| 7319 |
+
{'id': placeholder, 'type': str(manifest.get('type') or 'IMMUTABLE')}
|
| 7320 |
+
for placeholder, manifest in ir['anchors'].items()
|
| 7321 |
+
],
|
| 7322 |
+
})
|
| 7323 |
+
strict = (
|
| 7324 |
+
' This is a strict skeleton retry. Copy every [[SP-Gxxxxxx]] placeholder byte-for-byte, '
|
| 7325 |
+
'exactly once, and in the same order. Do not translate, delete, duplicate, rename, or move placeholders.'
|
| 7326 |
+
if attempt > 1 else ''
|
| 7327 |
+
)
|
| 7328 |
+
response = self.call_deepseek(
|
| 7329 |
+
(
|
| 7330 |
+
f'Translate the supplied protected research-concept surfaces into {target_lang}. {rule} '
|
| 7331 |
+
'SP-G placeholders are opaque server-owned technical identities: preserve every placeholder '
|
| 7332 |
+
'byte-for-byte, exactly once, and in the same order; translate only the linguistic surface around them. '
|
| 7333 |
+
'Do not infer or spell out what a placeholder represents. Return JSON only as '
|
| 7334 |
+
'{"terms":[{"term_id":"G000001","target":"..."}]} and return every supplied term_id exactly once.'
|
| 7335 |
+
+ strict
|
| 7336 |
+
),
|
| 7337 |
+
json.dumps({'ir_version': self.CANONICAL_GLOSSARY_IR_VERSION, 'terms': payload}, ensure_ascii=False),
|
| 7338 |
+
max_tokens=max(1200, min(5000, 120 + len(payload) * 90)),
|
| 7339 |
+
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 7340 |
+
)
|
| 7341 |
+
parsed = self._extract_json_from_text(response)
|
| 7342 |
+
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
| 7343 |
+
returned = {
|
| 7344 |
+
str(item.get('term_id') or '').strip(): re.sub(r'\s+', ' ', str(item.get('target') or '')).strip()
|
| 7345 |
+
for item in items if isinstance(item, dict)
|
| 7346 |
+
}
|
| 7347 |
+
next_pending = []
|
| 7348 |
+
for term_id in pending:
|
| 7349 |
+
protected_target = returned.get(term_id, '')
|
| 7350 |
+
try:
|
| 7351 |
+
if not protected_target:
|
| 7352 |
+
raise DeepSeekRequestError(
|
| 7353 |
+
f'Canonical glossary protected IR omitted term_id={term_id}.'
|
| 7354 |
+
)
|
| 7355 |
+
restored = self._restore_canonical_glossary_ir_target(
|
| 7356 |
+
protected_target, ir_by_id[term_id], block_id=f'canonical:{term_id}'
|
| 7357 |
+
)
|
| 7358 |
+
defects = self._canonical_target_deterministic_defects(
|
| 7359 |
+
source_by_id[term_id], restored, target_lang
|
| 7360 |
+
)
|
| 7361 |
+
anchor_defects = [d for d in defects if d.startswith('PROTECTED_ANCHOR_')]
|
| 7362 |
+
if anchor_defects:
|
| 7363 |
+
raise DeepSeekRequestError(
|
| 7364 |
+
f'Canonical glossary protected IR restoration violated anchor contract: '
|
| 7365 |
+
f'{term_id} defects={anchor_defects}'
|
| 7366 |
+
)
|
| 7367 |
+
resolved[source_by_id[term_id]] = restored
|
| 7368 |
+
except DeepSeekRequestError:
|
| 7369 |
+
if attempt >= self.MAX_CANONICAL_GLOSSARY_IR_ATTEMPTS:
|
| 7370 |
+
raise
|
| 7371 |
+
next_pending.append(term_id)
|
| 7372 |
+
pending = next_pending
|
| 7373 |
+
if pending:
|
| 7374 |
+
raise DeepSeekRequestError(
|
| 7375 |
+
'Canonical glossary protected IR exhausted with unresolved terms: ' + ', '.join(pending[:3])
|
| 7376 |
+
)
|
| 7377 |
+
return resolved
|
| 7378 |
+
|
| 7379 |
def seed_canonical_concept_glossary(self, concept_ledger, target_lang, existing=None):
|
| 7380 |
+
"""Translate/freeze canonical task concepts through protected IR.
|
| 7381 |
+
|
| 7382 |
+
R6-R7 moves exact technical identities out of model ownership. Existing
|
| 7383 |
+
safe glossary entries remain frozen; missing or deterministic-unsafe
|
| 7384 |
+
entries are rebuilt through SP-G protected IR before quality review.
|
| 7385 |
+
"""
|
| 7386 |
mapping = dict(existing or {})
|
| 7387 |
rows = (dict(concept_ledger or {}).get('items') or []) if isinstance(concept_ledger, dict) else []
|
| 7388 |
sources = []
|
|
|
|
| 7402 |
flexible_sources.append(term)
|
| 7403 |
else:
|
| 7404 |
strict_sources.append(term)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7405 |
self._canonical_concept_sources = set(strict_sources)
|
| 7406 |
self._canonical_flexible_sources = set(flexible_sources)
|
| 7407 |
self._canonical_concept_kinds = dict(source_kinds)
|
| 7408 |
if not sources:
|
| 7409 |
return mapping
|
| 7410 |
|
| 7411 |
+
# Rebuild only entries that are missing or whose server-owned identity
|
| 7412 |
+
# invariants are already broken. Safe historical targets remain frozen.
|
| 7413 |
+
rebuild = []
|
| 7414 |
+
for term in sources:
|
| 7415 |
+
current = re.sub(r'\s+', ' ', str(mapping.get(term) or '')).strip()
|
| 7416 |
+
if not current:
|
| 7417 |
+
rebuild.append(term)
|
| 7418 |
+
continue
|
| 7419 |
+
if term in strict_sources:
|
| 7420 |
+
defects = self._canonical_target_deterministic_defects(term, current, target_lang)
|
| 7421 |
+
if defects:
|
| 7422 |
+
rebuild.append(term)
|
| 7423 |
+
mapping.pop(term, None)
|
| 7424 |
+
self._record_translation_assurance(
|
| 7425 |
+
'CANONICAL_GLOSSARY_STALE_TARGET_REBUILD', target_lang=target_lang,
|
| 7426 |
+
severity='repair', decision='REPAIR_LOCAL',
|
| 7427 |
+
detail={'source': term[:120], 'defects': ','.join(defects[:4])},
|
| 7428 |
+
)
|
| 7429 |
+
continue
|
| 7430 |
+
elif self._canonical_anchor_drift_defects(term, current):
|
| 7431 |
+
rebuild.append(term)
|
| 7432 |
+
mapping.pop(term, None)
|
| 7433 |
+
|
| 7434 |
translate_terms = []
|
| 7435 |
+
for term in rebuild:
|
| 7436 |
compact = re.sub(r'[^A-Za-z0-9]', '', term)
|
| 7437 |
+
technical = bool(compact) and (
|
| 7438 |
+
compact.isupper() or any(ch.isdigit() for ch in compact)
|
| 7439 |
+
or (len(compact) > 2 and any(ch.isupper() for ch in compact[1:]))
|
| 7440 |
+
) and ' ' not in term
|
| 7441 |
if technical:
|
| 7442 |
mapping[term] = term
|
| 7443 |
else:
|
| 7444 |
translate_terms.append(term)
|
| 7445 |
if translate_terms:
|
| 7446 |
+
translated = self._translate_canonical_glossary_ir_terms(translate_terms, target_lang)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7447 |
for term in translate_terms:
|
| 7448 |
+
target = re.sub(r'\s+', ' ', str(translated.get(term) or '')).strip()
|
| 7449 |
+
if not target:
|
| 7450 |
+
raise DeepSeekRequestError(f'Canonical concept glossary omitted term after protected IR: {term[:80]}')
|
| 7451 |
if target.casefold() == term.casefold() and re.search(r'[a-z]{4,}', term):
|
| 7452 |
raise DeepSeekRequestError(f'Canonical concept remained untranslated: {term[:80]}')
|
| 7453 |
mapping[term] = target
|
|
|
|
| 7512 |
|
| 7513 |
if cls._canonical_target_surface_suspicious(target_value, target_lang):
|
| 7514 |
defects.append('MALFORMED_SURFACE')
|
| 7515 |
+
defects.extend(cls._canonical_anchor_drift_defects(source_value, target_value))
|
|
|
|
| 7516 |
return list(dict.fromkeys(defects))
|
| 7517 |
|
| 7518 |
def validate_and_refine_canonical_concept_glossary(self, concept_ledger, target_lang, glossary):
|
| 7519 |
+
"""Review strict canonical terms through the same protected IR contract.
|
| 7520 |
+
|
| 7521 |
+
The secondary reviewer never receives raw exact anchor values. It may
|
| 7522 |
+
only judge/repair the linguistic surface around opaque SP-G placeholders.
|
| 7523 |
+
Server-side deterministic invariants remain the final authority.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7524 |
"""
|
| 7525 |
mapping = dict(glossary or {})
|
| 7526 |
rows = (dict(concept_ledger or {}).get('items') or []) if isinstance(concept_ledger, dict) else []
|
|
|
|
| 7536 |
if not strict_sources:
|
| 7537 |
return mapping
|
| 7538 |
|
| 7539 |
+
source_by_id = {}
|
| 7540 |
+
id_by_source = {}
|
| 7541 |
+
ir_by_source = {}
|
| 7542 |
+
payload = []
|
| 7543 |
+
candidate_defects = {}
|
| 7544 |
+
protected_candidates = {}
|
| 7545 |
+
for index, source in enumerate(strict_sources, start=1):
|
| 7546 |
+
term_id = f'G{index:06d}'
|
| 7547 |
+
source_by_id[term_id] = source
|
| 7548 |
+
id_by_source[source] = term_id
|
| 7549 |
+
ir = self._compile_canonical_glossary_ir(source)
|
| 7550 |
+
ir_by_source[source] = ir
|
| 7551 |
+
candidate = re.sub(r'\s+', ' ', str(mapping.get(source) or '')).strip()
|
| 7552 |
+
defects = self._canonical_target_deterministic_defects(source, candidate, target_lang)
|
| 7553 |
+
candidate_defects[source] = defects
|
| 7554 |
+
protected_candidate = ''
|
| 7555 |
+
if candidate and not any(code.startswith('PROTECTED_ANCHOR_') for code in defects):
|
| 7556 |
+
try:
|
| 7557 |
+
protected_candidate = self._protect_existing_canonical_target_with_ir(
|
| 7558 |
+
candidate, ir, block_id=f'canonical-review:{term_id}'
|
| 7559 |
+
)
|
| 7560 |
+
except DeepSeekRequestError:
|
| 7561 |
+
# Treat projection failure as an anchor-integrity defect. In
|
| 7562 |
+
# normal production R6-R7 seed rebuilds such a stale target
|
| 7563 |
+
# before review; direct callers remain fail-closed unless the
|
| 7564 |
+
# reviewer supplies a fresh protected correction.
|
| 7565 |
+
if 'PROTECTED_ANCHOR_LOSS' not in defects:
|
| 7566 |
+
defects = list(defects) + ['PROTECTED_ANCHOR_LOSS']
|
| 7567 |
+
candidate_defects[source] = list(dict.fromkeys(defects))
|
| 7568 |
+
protected_candidates[source] = protected_candidate
|
| 7569 |
+
payload.append({
|
| 7570 |
+
'term_id': term_id,
|
| 7571 |
+
'protected_source': ir['protected_source'],
|
| 7572 |
+
'candidate_target': protected_candidate,
|
| 7573 |
+
'deterministic_defects': list(candidate_defects[source]),
|
| 7574 |
+
'anchor_manifest': [
|
| 7575 |
+
{'id': placeholder, 'type': str(manifest.get('type') or 'IMMUTABLE')}
|
| 7576 |
+
for placeholder, manifest in ir['anchors'].items()
|
| 7577 |
+
],
|
| 7578 |
+
})
|
| 7579 |
+
|
| 7580 |
empty_sources = [source for source, defects in candidate_defects.items() if 'EMPTY_TARGET' in defects]
|
| 7581 |
if empty_sources:
|
| 7582 |
raise DeepSeekRequestError(
|
|
|
|
| 7587 |
try:
|
| 7588 |
response = self.call_deepseek(
|
| 7589 |
(
|
| 7590 |
+
f'Independently review strict research-construct translations for {target_lang}. {language_rule} '
|
| 7591 |
+
'Judge semantic identity and natural academic terminology, not stylistic preference. '
|
| 7592 |
+
'SP-G placeholders are opaque server-owned technical identities. Never infer, translate, delete, duplicate, '
|
| 7593 |
+
'rename, or reorder them. Preserve every placeholder byte-for-byte, exactly once, and in the same order. '
|
| 7594 |
+
'If candidate_target is empty, construct one safe target from protected_source. If the candidate is sound, '
|
| 7595 |
+
'return it unchanged with accepted=true. If it is materially flawed, return exactly one corrected protected '
|
| 7596 |
+
'target with accepted=false. Do not merge distinct constructs. Return JSON only as '
|
| 7597 |
+
'{"terms":[{"term_id":"G000001","accepted":true,"target":"..."}]} and return every term_id exactly once.'
|
| 7598 |
),
|
| 7599 |
+
json.dumps({'ir_version': self.CANONICAL_GLOSSARY_IR_VERSION, 'terms': payload}, ensure_ascii=False),
|
| 7600 |
max_tokens=max(1200, min(4200, 180 + len(payload) * 120)),
|
| 7601 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 7602 |
)
|
|
|
|
| 7620 |
for item in items:
|
| 7621 |
if not isinstance(item, dict):
|
| 7622 |
continue
|
| 7623 |
+
term_id = str(item.get('term_id') or '').strip()
|
| 7624 |
+
if term_id in source_by_id:
|
| 7625 |
+
returned[term_id] = item
|
| 7626 |
extra_count = sum(
|
| 7627 |
1 for item in items if isinstance(item, dict)
|
| 7628 |
+
and str(item.get('term_id') or '').strip() not in source_by_id
|
| 7629 |
)
|
| 7630 |
if extra_count:
|
| 7631 |
self._record_translation_assurance(
|
|
|
|
| 7635 |
|
| 7636 |
refined = dict(mapping)
|
| 7637 |
for source in strict_sources:
|
| 7638 |
+
term_id = id_by_source[source]
|
| 7639 |
+
candidate = re.sub(r'\s+', ' ', str(mapping.get(source) or '')).strip()
|
| 7640 |
defects = list(candidate_defects.get(source) or [])
|
| 7641 |
+
protected_candidate = str(protected_candidates.get(source) or '')
|
| 7642 |
+
# Populate lazily because a candidate may have been anchor-unsafe.
|
| 7643 |
+
if not protected_candidate and candidate and not any(code.startswith('PROTECTED_ANCHOR_') for code in defects):
|
| 7644 |
+
try:
|
| 7645 |
+
protected_candidate = self._protect_existing_canonical_target_with_ir(
|
| 7646 |
+
candidate, ir_by_source[source], block_id=f'canonical-review:{term_id}'
|
| 7647 |
+
)
|
| 7648 |
+
except DeepSeekRequestError:
|
| 7649 |
+
protected_candidate = ''
|
| 7650 |
+
item = returned.get(term_id)
|
| 7651 |
if not item:
|
| 7652 |
if defects:
|
| 7653 |
raise DeepSeekRequestError(
|
|
|
|
| 7662 |
|
| 7663 |
accepted_raw = item.get('accepted')
|
| 7664 |
accepted = accepted_raw is True or str(accepted_raw).strip().casefold() == 'true'
|
| 7665 |
+
reviewed_protected = re.sub(r'\s+', ' ', str(item.get('target') or '')).strip()
|
| 7666 |
+
reviewed_target = ''
|
| 7667 |
+
reviewed_defects = ['EMPTY_TARGET']
|
| 7668 |
+
if reviewed_protected:
|
| 7669 |
+
try:
|
| 7670 |
+
reviewed_target = self._restore_canonical_glossary_ir_target(
|
| 7671 |
+
reviewed_protected, ir_by_source[source], block_id=f'canonical-review:{term_id}'
|
| 7672 |
+
)
|
| 7673 |
+
reviewed_defects = self._canonical_target_deterministic_defects(
|
| 7674 |
+
source, reviewed_target, target_lang
|
| 7675 |
+
)
|
| 7676 |
+
except DeepSeekRequestError:
|
| 7677 |
+
reviewed_target = ''
|
| 7678 |
+
reviewed_defects = ['PROTECTED_IR_SKELETON_MISMATCH']
|
| 7679 |
|
| 7680 |
+
if accepted and reviewed_protected in {'', protected_candidate}:
|
|
|
|
|
|
|
|
|
|
| 7681 |
if not defects:
|
| 7682 |
continue
|
| 7683 |
raise DeepSeekRequestError(
|
|
|
|
| 7696 |
continue
|
| 7697 |
|
| 7698 |
if not defects:
|
|
|
|
|
|
|
|
|
|
| 7699 |
reason = 'NON_ACTIONABLE_REJECTION'
|
| 7700 |
+
if accepted and reviewed_protected and reviewed_protected != protected_candidate:
|
| 7701 |
reason = 'ACCEPTED_BUT_CHANGED'
|
| 7702 |
+
elif reviewed_protected and reviewed_protected != protected_candidate and reviewed_defects:
|
| 7703 |
reason = 'INVALID_CORRECTION'
|
| 7704 |
self._record_translation_assurance(
|
| 7705 |
'CANONICAL_GLOSSARY_REVIEW_INCONCLUSIVE', target_lang=target_lang, severity='diagnostic',
|