Spaces:
Running
Running
S0-R1-C5.5-R6-R9 Translation Semantic Object Frame
#97
by GuXiaopeng - opened
- core/generation_runner.py +1 -1
- core/pipeline_agent.py +228 -23
core/generation_runner.py
CHANGED
|
@@ -45,7 +45,7 @@ CITATION_ROLE_RULES = {
|
|
| 45 |
"methods": (2, 280),
|
| 46 |
"discussion": (2, 220),
|
| 47 |
}
|
| 48 |
-
TRANSLATION_CONTRACT_VERSION = 'hierarchical-multilingual-
|
| 49 |
FINAL_CITATION_LEDGER_VERSION = 'resolved-final-ledger-v1'
|
| 50 |
ENGLISH_SECTION_SEAL_VERSION = 'english-section-integrity-seal-v1'
|
| 51 |
|
|
|
|
| 45 |
"methods": (2, 280),
|
| 46 |
"discussion": (2, 220),
|
| 47 |
}
|
| 48 |
+
TRANSLATION_CONTRACT_VERSION = 'hierarchical-multilingual-v9'
|
| 49 |
FINAL_CITATION_LEDGER_VERSION = 'resolved-final-ledger-v1'
|
| 50 |
ENGLISH_SECTION_SEAL_VERSION = 'english-section-integrity-seal-v1'
|
| 51 |
|
core/pipeline_agent.py
CHANGED
|
@@ -70,7 +70,8 @@ class CitationAuditError(DeepSeekRequestError):
|
|
| 70 |
|
| 71 |
|
| 72 |
class SSciPipelineAgent:
|
| 73 |
-
TRANSLATION_CONTRACT_VERSION = 'hierarchical-multilingual-
|
|
|
|
| 74 |
MAX_TRANSLATION_CLAIM_REPAIR_ATTEMPTS = 2
|
| 75 |
MAX_TRANSLATION_CLAIM_TOTAL_REPAIRS = 6
|
| 76 |
PROTECTED_ENTITY_LEDGER_VERSION = 'protected-entity-ledger-v2'
|
|
@@ -6890,6 +6891,116 @@ TEXT:
|
|
| 6890 |
frame["frame_hash"] = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
| 6891 |
return frame
|
| 6892 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6893 |
@classmethod
|
| 6894 |
def _translation_claim_units(cls, value):
|
| 6895 |
"""Create stable source-side claim IDs without constraining target punctuation."""
|
|
@@ -8201,7 +8312,7 @@ SECTION: {section_name}
|
|
| 8201 |
'ACTOR_CHANGED','RELATION_CHANGED','OBJECT_CHANGED','POLARITY_CHANGED',
|
| 8202 |
'CAUSALITY_STRENGTH_CHANGED','MODALITY_UNCERTAINTY_CHANGED',
|
| 8203 |
'CONDITION_SCOPE_CHANGED','COMPARISON_DIRECTION_CHANGED','TEMPORAL_SCOPE_CHANGED',
|
| 8204 |
-
'CLAIM_MISSING','CLAIM_ADDED',
|
| 8205 |
}
|
| 8206 |
allowed_grammar_evidence = {
|
| 8207 |
'CASE_GOVERNMENT','AGREEMENT','MORPHOLOGY','VERB_FORM','WORD_ORDER',
|
|
@@ -8228,6 +8339,7 @@ SECTION: {section_name}
|
|
| 8228 |
'english': item['text'],
|
| 8229 |
'target': target_claims[claim_id],
|
| 8230 |
'source_semantic_frame': self._source_semantic_claim_frame(item['text'], section_name),
|
|
|
|
| 8231 |
})
|
| 8232 |
|
| 8233 |
issues = {}
|
|
@@ -8245,16 +8357,18 @@ SECTION: {section_name}
|
|
| 8245 |
'target_language': target_lang,
|
| 8246 |
'glossary': relevant_glossary,
|
| 8247 |
'allowed_issue_codes': sorted(allowed_codes),
|
| 8248 |
-
'
|
|
|
|
| 8249 |
}
|
| 8250 |
profile = self._translation_language_profile(target_lang)
|
| 8251 |
lexical_contract = self._translation_lexical_retention_contract(target_lang)
|
| 8252 |
system_prompt = f"""You are a bilingual academic translation QA classifier for {target_lang}.
|
| 8253 |
-
Review EACH claim independently. Do not rewrite and do not provide free-form explanations. The supplied
|
|
|
|
| 8254 |
LANGUAGE ADAPTER: {profile['adapter_id']}; morphology={profile['morphology']}. {profile['term_policy']} {lexical_contract} {profile['contamination_policy']}
|
| 8255 |
For Russian, do not flag a terminology error merely because a translated term is inflected for grammatical case/number/gender; sustained non-Russian prose remains an additional contamination signal. For Kazakh, do not flag a terminology error merely because productive suffixes attach to the correct term stem; clearly Russian lexical/syntactic intrusion remains an additional language-specific signal when the pair supports that conclusion. Cyrillic script or absence of Kazakh-specific letters alone is never sufficient evidence.
|
| 8256 |
-
Return valid JSON only in this exact form: {{"items":[{{"id":"SP-P000001-C000001","ok":true,"codes":[],"evidence_codes":[]}}]}}.
|
| 8257 |
-
If ok=false, codes MUST contain only values from allowed_issue_codes. For
|
| 8258 |
response = self.call_deepseek(
|
| 8259 |
system_prompt, json.dumps(payload, ensure_ascii=False),
|
| 8260 |
max_tokens=max(1200, min(3600, 650 + 260 * len(rows))),
|
|
@@ -8288,6 +8402,14 @@ If ok=false, codes MUST contain only values from allowed_issue_codes. For SEMANT
|
|
| 8288 |
'SEMANTIC_DRIFT','NEGATION_DRIFT','CAUSALITY_STRENGTH_DRIFT',
|
| 8289 |
'UNCERTAINTY_DRIFT','OMITTED_CLAIM','ADDED_CLAIM',
|
| 8290 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8291 |
if any(code in semantic_code_set for code in codes):
|
| 8292 |
derived = {
|
| 8293 |
'NEGATION_DRIFT': 'POLARITY_CHANGED',
|
|
@@ -8300,12 +8422,22 @@ If ok=false, codes MUST contain only values from allowed_issue_codes. For SEMANT
|
|
| 8300 |
fallback = derived.get(issue_code)
|
| 8301 |
if fallback and fallback not in evidence_codes:
|
| 8302 |
evidence_codes.append(fallback)
|
| 8303 |
-
|
| 8304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8305 |
evidence_row = self._translation_claim_qa_evidence.setdefault(
|
| 8306 |
(meta['block_id'], meta['claim_id']), {}
|
| 8307 |
)
|
| 8308 |
evidence_row['semantic_delta_codes'] = evidence_codes[:6]
|
|
|
|
|
|
|
|
|
|
| 8309 |
if 'GRAMMAR' in codes:
|
| 8310 |
if not grammar_evidence_codes:
|
| 8311 |
grammar_evidence_codes = ['GRAMMAR_UNSPECIFIED']
|
|
@@ -8440,7 +8572,15 @@ If ok=false, codes MUST contain only values from allowed_issue_codes. For SEMANT
|
|
| 8440 |
)
|
| 8441 |
if only_suppressed_contamination:
|
| 8442 |
continue
|
| 8443 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8444 |
issues.setdefault(meta['block_id'], {})[meta['claim_id']] = list(dict.fromkeys(codes))[:5]
|
| 8445 |
return issues
|
| 8446 |
|
|
@@ -8804,6 +8944,12 @@ Translate headings and terminology naturally. Preserve standard variable abbrevi
|
|
| 8804 |
'grammar_evidence_codes': sorted({str(code or '').strip().upper() for code in (evidence.get('grammar_evidence_codes') or []) if str(code or '').strip()}),
|
| 8805 |
'contamination_evidence_codes': sorted({str(code or '').strip().upper() for code in (evidence.get('contamination_evidence_codes') or []) if str(code or '').strip()}),
|
| 8806 |
'detected_unprotected_english_tokens': sorted({str(token or '').strip().casefold() for token in (evidence.get('detected_unprotected_english_tokens') or []) if str(token or '').strip()}),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8807 |
}
|
| 8808 |
payload = json.dumps(bounded, ensure_ascii=False, sort_keys=True, separators=(',', ':'))
|
| 8809 |
return hashlib.sha256(payload.encode('utf-8')).hexdigest()
|
|
@@ -8864,7 +9010,12 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 8864 |
strategy = str(repair_strategy or 'LOCAL_EDIT').strip().upper()
|
| 8865 |
if strategy not in {'LOCAL_EDIT', 'SOURCE_REGENERATE'}:
|
| 8866 |
strategy = 'LOCAL_EDIT'
|
| 8867 |
-
evidence_payload = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8868 |
if strategy == 'LOCAL_EDIT':
|
| 8869 |
evidence_payload['current_target_claim'] = str(current_target_claim or '').strip()
|
| 8870 |
lexical_tokens = [
|
|
@@ -8879,6 +9030,12 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 8879 |
]
|
| 8880 |
if semantic_delta_codes:
|
| 8881 |
evidence_payload['semantic_delta_codes'] = semantic_delta_codes[:6]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8882 |
grammar_evidence_codes = [
|
| 8883 |
str(code or '').strip().upper() for code in (supplied_evidence.get('grammar_evidence_codes') or [])
|
| 8884 |
if str(code or '').strip()
|
|
@@ -8903,7 +9060,7 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 8903 |
+ '. Use the bounded detector evidence below only to repair the supplied claim; do not invent additional defects. '
|
| 8904 |
'For LOCAL_EDIT, the current target claim is context, not authority; for SOURCE_REGENERATE it is intentionally absent. The frozen English source is always authoritative. '
|
| 8905 |
'If detected_unprotected_english_tokens is non-empty, every listed token is deterministic lexical-purity evidence from a prior target and MUST NOT survive as ordinary untranslated target-language prose unless represented by a protected SP-T anchor. '
|
| 8906 |
-
'If semantic_delta_codes is non-empty, enforce ONLY those evidenced semantic dimensions against the frozen English source and preserve all unaffected actor, relation, object, polarity, modality, condition, comparison, temporal scope, and evidentiary strength. UNRESOLVED_SEMANTIC_DELTA is a signal to regenerate faithfully from source, not an instruction to guess a specific correction. '
|
| 8907 |
'If grammar_evidence_codes is non-empty, repair or avoid only those bounded grammatical defect classes; GRAMMAR_UNSPECIFIED requires a fresh natural target-language rendering rather than blind micro-editing. '
|
| 8908 |
'If contamination_evidence_codes identifies KK_RUSSIAN_LEXICAL_INTRUSION or KK_RUSSIAN_SYNTACTIC_INTRUSION, remove only the evidenced Russian substitution/calque while preserving the frozen English meaning; do not invent an English lexical defect when deterministic English-token evidence is absent. '
|
| 8909 |
'Preserve every SP-P, SP-C, and SP-T marker byte-for-byte. Do not add claims or factual content. Return only the marked translated claim.'
|
|
@@ -9014,6 +9171,9 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 9014 |
'source_hash': hashlib.sha256(str(item['text']).encode('utf-8')).hexdigest(),
|
| 9015 |
'semantic_frame': cls._source_semantic_claim_frame(item['text'], section_name),
|
| 9016 |
'semantic_frame_hash': cls._source_semantic_claim_frame(item['text'], section_name).get('frame_hash', ''),
|
|
|
|
|
|
|
|
|
|
| 9017 |
'target_text': target_text,
|
| 9018 |
'qa_status': 'repair_pending' if codes else 'passed',
|
| 9019 |
'issue_codes': [str(code or '').strip().upper() for code in codes if str(code or '').strip()][:5],
|
|
@@ -9058,11 +9218,16 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 9058 |
row = states.get(key)
|
| 9059 |
expected_hash = hashlib.sha256(str(item['text']).encode('utf-8')).hexdigest()
|
| 9060 |
expected_semantic_hash = cls._source_semantic_claim_frame(item['text'], section_name).get('frame_hash', '')
|
|
|
|
| 9061 |
if not isinstance(row, dict) or row.get('source_hash') != expected_hash or not str(row.get('target_text') or '').strip():
|
| 9062 |
return {}, {}, False
|
| 9063 |
-
# R6-
|
| 9064 |
-
#
|
| 9065 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9066 |
stored_semantic_hash = str(row.get('semantic_frame_hash') or '')
|
| 9067 |
if stored_semantic_hash and stored_semantic_hash != expected_semantic_hash:
|
| 9068 |
return {}, {}, False
|
|
@@ -9177,11 +9342,25 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 9177 |
for item in (repair_evidence.get('grammar_evidence_codes') or [])
|
| 9178 |
if str(item or '').strip()
|
| 9179 |
] or ['GRAMMAR_UNSPECIFIED']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9180 |
rows.append({
|
| 9181 |
'id': f'SP-P{block_id}-{claim_id}', 'block_id': block_id,
|
| 9182 |
'claim_id': claim_id, 'code': code, 'english': source_claims[claim_id],
|
| 9183 |
'target': target_claims[claim_id], 'deterministic_confirmed': False,
|
| 9184 |
'grammar_evidence_codes': grammar_evidence_codes[:4],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9185 |
})
|
| 9186 |
|
| 9187 |
confirmed = {}
|
|
@@ -9202,8 +9381,10 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 9202 |
return confirmed
|
| 9203 |
|
| 9204 |
allowed_evidence = {
|
| 9205 |
-
'CLAIM_MISSING','CLAIM_ADDED','
|
| 9206 |
-
'
|
|
|
|
|
|
|
| 9207 |
'GRAMMATICAL_VIOLATION','CASE_GOVERNMENT','AGREEMENT','MORPHOLOGY','VERB_FORM',
|
| 9208 |
'SYNTAX_STRUCTURE','PUNCTUATION','SYNTACTIC_COMPLETENESS','GRAMMAR_UNSPECIFIED',
|
| 9209 |
'WORD_ORDER_UNGRAMMATICAL','WORD_ORDER_SEMANTIC_SCOPE_CHANGED',
|
|
@@ -9228,6 +9409,9 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 9228 |
},
|
| 9229 |
'contamination_evidence_codes': list(row.get('contamination_evidence_codes') or [])[:2],
|
| 9230 |
'grammar_evidence_codes': list(row.get('grammar_evidence_codes') or [])[:4],
|
|
|
|
|
|
|
|
|
|
| 9231 |
} for row in model_rows],
|
| 9232 |
}
|
| 9233 |
lexical_contract = self._translation_lexical_retention_contract(target_lang)
|
|
@@ -9236,6 +9420,7 @@ This is verification-only: do not rewrite, suggest wording, or emit free-form ex
|
|
| 9236 |
{lexical_contract}
|
| 9237 |
For Russian, allow required case/number/gender inflection and retain sustained non-Russian prose as an additional contamination signal. For Kazakh, allow productive case, possessive, person/plural and vowel-harmony suffixes on the correct term stem, but treat clearly Russian lexical/syntactic substitution as blocking only when the pair itself supports that conclusion. For Chinese, allow natural reordering and sentence reshaping inside the same claim while preserving meaning.
|
| 9238 |
For LANGUAGE_CONTAMINATION, deterministic_lexical is authoritative for ordinary English/Latin residue: when confirmed=false and token_count=0, you MUST NOT confirm UNTRANSLATED_LEXEME or UNTRANSLATED_PROSE. For Kazakh, a non-Latin contamination blocker may be confirmed only when contamination_evidence_codes contains KK_RUSSIAN_LEXICAL_INTRUSION or KK_RUSSIAN_SYNTACTIC_INTRUSION, and the evidence_code must match one of those supplied typed codes. The per-item glossary lists the frozen target terms that are legal for that source claim.
|
|
|
|
| 9239 |
For GRAMMAR, grammar_evidence_codes is the typed defect history and must remain continuous into this final decision. Never use generic GRAMMATICAL_VIOLATION as final blocking evidence when a typed grammar history exists. If WORD_ORDER is supplied, non-canonical or stylistically different ordering is NOT blocking. Confirm only when the target itself is ungrammatical because of ordering (WORD_ORDER_UNGRAMMATICAL) or when ordering changes modifier/negation/modality/condition/relation scope relative to the English claim (WORD_ORDER_SEMANTIC_SCOPE_CHANGED). For CASE_GOVERNMENT, AGREEMENT, MORPHOLOGY, VERB_FORM, SYNTAX_STRUCTURE, PUNCTUATION, or SYNTACTIC_COMPLETENESS, a blocking evidence_code must match the supplied typed class. If only GRAMMAR_UNSPECIFIED was supplied, you may narrow it to one of those specific classes or one of the two WORD_ORDER consequence codes; do not confirm a generic grammar label.
|
| 9240 |
Return valid JSON only: {{"items":[{{"id":"SP-P000001-C000001","issue_code":"GRAMMAR","decision":"CONFIRMED_BLOCKING","evidence_code":"AGREEMENT"}}]}}.
|
| 9241 |
Decision must be CONFIRMED_BLOCKING or NOT_CONFIRMED. evidence_code must be one of: {sorted(allowed_evidence)}. Return every supplied id+issue_code pair exactly once."""
|
|
@@ -9264,6 +9449,25 @@ Decision must be CONFIRMED_BLOCKING or NOT_CONFIRMED. evidence_code must be one
|
|
| 9264 |
evidence_code = str(item.get('evidence_code') or '').strip().upper()
|
| 9265 |
if decision not in {'CONFIRMED_BLOCKING', 'NOT_CONFIRMED'} or evidence_code not in allowed_evidence:
|
| 9266 |
raise DeepSeekRequestError(f'Translation QA adjudication returned invalid bounded decision for {row["id"]}.')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9267 |
if row['code'] == 'LANGUAGE_CONTAMINATION':
|
| 9268 |
lexical = dict(row.get('lexical_evidence') or {})
|
| 9269 |
typed = set(row.get('contamination_evidence_codes') or [])
|
|
@@ -9643,13 +9847,14 @@ Decision must be CONFIRMED_BLOCKING or NOT_CONFIRMED. evidence_code must be one
|
|
| 9643 |
semantic_deltas.append(fallback)
|
| 9644 |
if semantic_deltas:
|
| 9645 |
repair_evidence['semantic_delta_codes'] = list(dict.fromkeys(semantic_deltas))[:6]
|
| 9646 |
-
specific_semantic =
|
| 9647 |
-
|
| 9648 |
-
|
| 9649 |
-
|
| 9650 |
-
|
| 9651 |
-
|
| 9652 |
-
|
|
|
|
| 9653 |
if repair_evidence.get('semantic_delta_codes'):
|
| 9654 |
self._record_translation_assurance(
|
| 9655 |
'SEMANTIC_REPAIR_EVIDENCE', target_lang=target_lang,
|
|
|
|
| 70 |
|
| 71 |
|
| 72 |
class SSciPipelineAgent:
|
| 73 |
+
TRANSLATION_CONTRACT_VERSION = 'hierarchical-multilingual-v9'
|
| 74 |
+
TRANSLATION_SEMANTIC_OBJECT_VERSION = 'translation-semantic-object-v1'
|
| 75 |
MAX_TRANSLATION_CLAIM_REPAIR_ATTEMPTS = 2
|
| 76 |
MAX_TRANSLATION_CLAIM_TOTAL_REPAIRS = 6
|
| 77 |
PROTECTED_ENTITY_LEDGER_VERSION = 'protected-entity-ledger-v2'
|
|
|
|
| 6891 |
frame["frame_hash"] = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
| 6892 |
return frame
|
| 6893 |
|
| 6894 |
+
@classmethod
|
| 6895 |
+
def _translation_semantic_object_frame(cls, source_claim, section_name='', glossary=None):
|
| 6896 |
+
"""Compile one frozen English claim into a versioned semantic object.
|
| 6897 |
+
|
| 6898 |
+
R6-R9 moves translation QA away from an untyped whole-sentence verdict.
|
| 6899 |
+
The server owns this object before any target-language judgment: claim
|
| 6900 |
+
identity, evidence/causal/modality contract, protected factual object
|
| 6901 |
+
types, and canonical terminology ownership are frozen once and hashed.
|
| 6902 |
+
The model may judge target realization, but it cannot redefine source
|
| 6903 |
+
semantics or invent a new object schema on retry/resume.
|
| 6904 |
+
"""
|
| 6905 |
+
source = re.sub(r"\s+", " ", str(source_claim or '')).strip()
|
| 6906 |
+
semantic_frame = cls._source_semantic_claim_frame(source, section_name)
|
| 6907 |
+
_protected, mapping = cls._protect_translation_tokens(source, claim_id='C000001')
|
| 6908 |
+
type_counts = Counter(str(item.get('type') or 'IMMUTABLE') for item in mapping.values())
|
| 6909 |
+
render_counts = Counter(str(item.get('render_policy') or 'exact') for item in mapping.values())
|
| 6910 |
+
glossary_value = dict(glossary or {})
|
| 6911 |
+
canonical_terms = []
|
| 6912 |
+
folded = source.casefold()
|
| 6913 |
+
for source_term, target_term in sorted(glossary_value.items(), key=lambda item: str(item[0]).casefold()):
|
| 6914 |
+
source_term = re.sub(r"\s+", " ", str(source_term or '')).strip()
|
| 6915 |
+
target_term = re.sub(r"\s+", " ", str(target_term or '')).strip()
|
| 6916 |
+
if source_term and source_term.casefold() in folded:
|
| 6917 |
+
canonical_terms.append({
|
| 6918 |
+
'source': source_term,
|
| 6919 |
+
'target': target_term,
|
| 6920 |
+
'identity': hashlib.sha256(source_term.casefold().encode('utf-8')).hexdigest()[:16],
|
| 6921 |
+
})
|
| 6922 |
+
semantic_contract = {
|
| 6923 |
+
key: semantic_frame.get(key)
|
| 6924 |
+
for key in (
|
| 6925 |
+
'polarity','causal_strength','modality','claim_provenance','evidence_owner',
|
| 6926 |
+
'citation_requirement','citation_scope','citation_count'
|
| 6927 |
+
)
|
| 6928 |
+
}
|
| 6929 |
+
obj = {
|
| 6930 |
+
'version': cls.TRANSLATION_SEMANTIC_OBJECT_VERSION,
|
| 6931 |
+
'source_hash': hashlib.sha256(source.encode('utf-8')).hexdigest(),
|
| 6932 |
+
'semantic_contract': semantic_contract,
|
| 6933 |
+
'protected_object_counts': dict(sorted(type_counts.items())),
|
| 6934 |
+
'render_policy_counts': dict(sorted(render_counts.items())),
|
| 6935 |
+
}
|
| 6936 |
+
# Identity is source-owned. Target glossary realization is useful QA
|
| 6937 |
+
# context but must not change the semantic-object hash across languages.
|
| 6938 |
+
canonical = json.dumps(obj, ensure_ascii=False, sort_keys=True, separators=(',', ':'))
|
| 6939 |
+
obj['object_hash'] = hashlib.sha256(canonical.encode('utf-8')).hexdigest()
|
| 6940 |
+
obj['canonical_terms'] = canonical_terms[:16]
|
| 6941 |
+
return obj
|
| 6942 |
+
|
| 6943 |
+
@staticmethod
|
| 6944 |
+
def _translation_semantic_delta_objects(source_claim, target_claim, raw_deltas):
|
| 6945 |
+
"""Validate model-reported semantic deltas as source/target-bound objects.
|
| 6946 |
+
|
| 6947 |
+
A generic ``SEMANTIC_DRIFT`` label is not evidence. R6-R9 requires a
|
| 6948 |
+
narrow semantic dimension plus spans that actually belong to the frozen
|
| 6949 |
+
source and current target (except claim-added/missing, where one side may
|
| 6950 |
+
be empty). Invalid or over-broad evidence is ignored rather than being
|
| 6951 |
+
promoted to a fatal repair lineage.
|
| 6952 |
+
"""
|
| 6953 |
+
allowed = {
|
| 6954 |
+
'ACTOR_CHANGED','RELATION_CHANGED','OBJECT_CHANGED','CONDITION_SCOPE_CHANGED',
|
| 6955 |
+
'COMPARISON_DIRECTION_CHANGED','TEMPORAL_SCOPE_CHANGED','CLAIM_MISSING','CLAIM_ADDED',
|
| 6956 |
+
}
|
| 6957 |
+
source = re.sub(r"\s+", " ", str(source_claim or '')).strip()
|
| 6958 |
+
target = re.sub(r"\s+", " ", str(target_claim or '')).strip()
|
| 6959 |
+
source_folded, target_folded = source.casefold(), target.casefold()
|
| 6960 |
+
objects = []
|
| 6961 |
+
for raw in (raw_deltas or []):
|
| 6962 |
+
if not isinstance(raw, dict):
|
| 6963 |
+
continue
|
| 6964 |
+
dimension = str(raw.get('dimension') or '').strip().upper()
|
| 6965 |
+
if dimension not in allowed:
|
| 6966 |
+
continue
|
| 6967 |
+
source_span = re.sub(r"\s+", " ", str(raw.get('source_span') or '')).strip()
|
| 6968 |
+
target_span = re.sub(r"\s+", " ", str(raw.get('target_span') or '')).strip()
|
| 6969 |
+
if dimension == 'CLAIM_MISSING':
|
| 6970 |
+
if not source_span or source_span.casefold() not in source_folded:
|
| 6971 |
+
continue
|
| 6972 |
+
elif dimension == 'CLAIM_ADDED':
|
| 6973 |
+
if not target_span or target_span.casefold() not in target_folded:
|
| 6974 |
+
continue
|
| 6975 |
+
else:
|
| 6976 |
+
if not source_span or not target_span:
|
| 6977 |
+
continue
|
| 6978 |
+
if source_span.casefold() not in source_folded or target_span.casefold() not in target_folded:
|
| 6979 |
+
continue
|
| 6980 |
+
# Whole-sentence restatement is not narrow evidence except for very
|
| 6981 |
+
# short claims, where a smaller span may not exist naturally.
|
| 6982 |
+
if len(source) > 180 and source_span and len(source_span) > max(180, int(len(source) * 0.80)):
|
| 6983 |
+
continue
|
| 6984 |
+
if len(target) > 180 and target_span and len(target_span) > max(180, int(len(target) * 0.80)):
|
| 6985 |
+
continue
|
| 6986 |
+
identity_payload = json.dumps(
|
| 6987 |
+
{'dimension': dimension, 'source_span': source_span.casefold(), 'target_span': target_span.casefold()},
|
| 6988 |
+
ensure_ascii=False, sort_keys=True, separators=(',', ':')
|
| 6989 |
+
)
|
| 6990 |
+
objects.append({
|
| 6991 |
+
'dimension': dimension, 'source_span': source_span, 'target_span': target_span,
|
| 6992 |
+
'object_id': hashlib.sha256(identity_payload.encode('utf-8')).hexdigest()[:20],
|
| 6993 |
+
})
|
| 6994 |
+
deduped = []
|
| 6995 |
+
seen = set()
|
| 6996 |
+
for item in objects:
|
| 6997 |
+
key = (item['dimension'], item['object_id'])
|
| 6998 |
+
if key in seen:
|
| 6999 |
+
continue
|
| 7000 |
+
seen.add(key)
|
| 7001 |
+
deduped.append(item)
|
| 7002 |
+
return deduped[:6]
|
| 7003 |
+
|
| 7004 |
@classmethod
|
| 7005 |
def _translation_claim_units(cls, value):
|
| 7006 |
"""Create stable source-side claim IDs without constraining target punctuation."""
|
|
|
|
| 8312 |
'ACTOR_CHANGED','RELATION_CHANGED','OBJECT_CHANGED','POLARITY_CHANGED',
|
| 8313 |
'CAUSALITY_STRENGTH_CHANGED','MODALITY_UNCERTAINTY_CHANGED',
|
| 8314 |
'CONDITION_SCOPE_CHANGED','COMPARISON_DIRECTION_CHANGED','TEMPORAL_SCOPE_CHANGED',
|
| 8315 |
+
'CLAIM_MISSING','CLAIM_ADDED',
|
| 8316 |
}
|
| 8317 |
allowed_grammar_evidence = {
|
| 8318 |
'CASE_GOVERNMENT','AGREEMENT','MORPHOLOGY','VERB_FORM','WORD_ORDER',
|
|
|
|
| 8339 |
'english': item['text'],
|
| 8340 |
'target': target_claims[claim_id],
|
| 8341 |
'source_semantic_frame': self._source_semantic_claim_frame(item['text'], section_name),
|
| 8342 |
+
'semantic_object': self._translation_semantic_object_frame(item['text'], section_name, glossary_value),
|
| 8343 |
})
|
| 8344 |
|
| 8345 |
issues = {}
|
|
|
|
| 8357 |
'target_language': target_lang,
|
| 8358 |
'glossary': relevant_glossary,
|
| 8359 |
'allowed_issue_codes': sorted(allowed_codes),
|
| 8360 |
+
'semantic_object_version': self.TRANSLATION_SEMANTIC_OBJECT_VERSION,
|
| 8361 |
+
'items': [{'id': row['id'], 'english': row['english'], 'target': row['target'], 'source_semantic_frame': row['source_semantic_frame'], 'semantic_object': row['semantic_object']} for row in rows],
|
| 8362 |
}
|
| 8363 |
profile = self._translation_language_profile(target_lang)
|
| 8364 |
lexical_contract = self._translation_lexical_retention_contract(target_lang)
|
| 8365 |
system_prompt = f"""You are a bilingual academic translation QA classifier for {target_lang}.
|
| 8366 |
+
Review EACH claim independently. Do not rewrite and do not provide free-form explanations. The supplied semantic_object is the server-frozen source authority. Preserve its semantic_contract, protected-object ownership, and canonical terminology identities. Check semantic equivalence and language quality: omissions/additions, negation, uncertainty, association-versus-causation strength, terminology, grammar, language contamination, citation attachment, and acronym attachment.
|
| 8367 |
+
For a generic SEMANTIC_DRIFT finding, a label alone is NOT evidence. You MUST also return one or more narrow semantic_deltas. Each delta must use dimension ACTOR_CHANGED, RELATION_CHANGED, OBJECT_CHANGED, CONDITION_SCOPE_CHANGED, COMPARISON_DIRECTION_CHANGED, TEMPORAL_SCOPE_CHANGED, CLAIM_MISSING, or CLAIM_ADDED and quote short exact source_span/target_span evidence from the supplied pair. For CLAIM_MISSING target_span may be empty; for CLAIM_ADDED source_span may be empty. Do not return the whole sentence as a pseudo-span when a narrower phrase identifies the changed object. If you cannot identify a source/target-bound semantic object, do NOT emit SEMANTIC_DRIFT.
|
| 8368 |
LANGUAGE ADAPTER: {profile['adapter_id']}; morphology={profile['morphology']}. {profile['term_policy']} {lexical_contract} {profile['contamination_policy']}
|
| 8369 |
For Russian, do not flag a terminology error merely because a translated term is inflected for grammatical case/number/gender; sustained non-Russian prose remains an additional contamination signal. For Kazakh, do not flag a terminology error merely because productive suffixes attach to the correct term stem; clearly Russian lexical/syntactic intrusion remains an additional language-specific signal when the pair supports that conclusion. Cyrillic script or absence of Kazakh-specific letters alone is never sufficient evidence.
|
| 8370 |
+
Return valid JSON only in this exact form: {{"items":[{{"id":"SP-P000001-C000001","ok":true,"codes":[],"evidence_codes":[],"semantic_deltas":[]}}]}}.
|
| 8371 |
+
If ok=false, codes MUST contain only values from allowed_issue_codes. For NEGATION_DRIFT, CAUSALITY_STRENGTH_DRIFT, UNCERTAINTY_DRIFT, OMITTED_CLAIM, or ADDED_CLAIM, evidence_codes should identify the affected semantic dimension using only: {sorted(allowed_semantic_evidence)}. SEMANTIC_DRIFT additionally requires validated semantic_deltas as described above; never use a generic MEANING_CHANGED fallback. For GRAMMAR, evidence_codes should identify the narrow grammatical defect using only: {sorted(allowed_grammar_evidence)}. For LANGUAGE_CONTAMINATION, evidence_codes MUST identify the supported subtype using only: {sorted(allowed_contamination_evidence)}. Use UNTRANSLATED_LEXEME/UNTRANSLATED_PROSE only for Latin/English residue. For Kazakh, use KK_RUSSIAN_LEXICAL_INTRUSION or KK_RUSSIAN_SYNTACTIC_INTRUSION only when the English/target pair itself clearly supports Russian substitution or a Russian syntactic calque; Cyrillic script or absence of Kazakh-specific letters is never sufficient. Use the narrowest supported evidence; use GRAMMAR_UNSPECIFIED only when a grammatical defect is clear but cannot be narrowed. Do not invent an error dimension. Never return numbered prose, examples, quotes, or corrective wording. Never return replacement text. Return every supplied id exactly once."""
|
| 8372 |
response = self.call_deepseek(
|
| 8373 |
system_prompt, json.dumps(payload, ensure_ascii=False),
|
| 8374 |
max_tokens=max(1200, min(3600, 650 + 260 * len(rows))),
|
|
|
|
| 8402 |
'SEMANTIC_DRIFT','NEGATION_DRIFT','CAUSALITY_STRENGTH_DRIFT',
|
| 8403 |
'UNCERTAINTY_DRIFT','OMITTED_CLAIM','ADDED_CLAIM',
|
| 8404 |
}
|
| 8405 |
+
semantic_objects = self._translation_semantic_delta_objects(
|
| 8406 |
+
meta['english'], meta['target'], returned_row.get('semantic_deltas') or []
|
| 8407 |
+
)
|
| 8408 |
+
if semantic_objects:
|
| 8409 |
+
object_dimensions = [item['dimension'] for item in semantic_objects]
|
| 8410 |
+
for dimension in object_dimensions:
|
| 8411 |
+
if dimension not in evidence_codes:
|
| 8412 |
+
evidence_codes.append(dimension)
|
| 8413 |
if any(code in semantic_code_set for code in codes):
|
| 8414 |
derived = {
|
| 8415 |
'NEGATION_DRIFT': 'POLARITY_CHANGED',
|
|
|
|
| 8422 |
fallback = derived.get(issue_code)
|
| 8423 |
if fallback and fallback not in evidence_codes:
|
| 8424 |
evidence_codes.append(fallback)
|
| 8425 |
+
# R6-R9: a generic semantic verdict cannot create a repair
|
| 8426 |
+
# lineage without a source/target-bound semantic object.
|
| 8427 |
+
if 'SEMANTIC_DRIFT' in codes and not semantic_objects:
|
| 8428 |
+
codes = [code for code in codes if code != 'SEMANTIC_DRIFT']
|
| 8429 |
+
self._record_translation_assurance(
|
| 8430 |
+
'SEMANTIC_OBJECT_UNTYPED_QA_UNCONFIRMED', target_lang=target_lang, block_id=row_id,
|
| 8431 |
+
severity='diagnostic', decision='PASS_WITH_WARNING',
|
| 8432 |
+
detail={'semantic_object_version': self.TRANSLATION_SEMANTIC_OBJECT_VERSION},
|
| 8433 |
+
)
|
| 8434 |
evidence_row = self._translation_claim_qa_evidence.setdefault(
|
| 8435 |
(meta['block_id'], meta['claim_id']), {}
|
| 8436 |
)
|
| 8437 |
evidence_row['semantic_delta_codes'] = evidence_codes[:6]
|
| 8438 |
+
evidence_row['semantic_object_deltas'] = semantic_objects[:6]
|
| 8439 |
+
evidence_row['semantic_object_hash'] = str(meta['semantic_object'].get('object_hash') or '')
|
| 8440 |
+
evidence_row['semantic_object_version'] = self.TRANSLATION_SEMANTIC_OBJECT_VERSION
|
| 8441 |
if 'GRAMMAR' in codes:
|
| 8442 |
if not grammar_evidence_codes:
|
| 8443 |
grammar_evidence_codes = ['GRAMMAR_UNSPECIFIED']
|
|
|
|
| 8572 |
)
|
| 8573 |
if only_suppressed_contamination:
|
| 8574 |
continue
|
| 8575 |
+
# R6-R9: model rejection without a typed actionable defect is
|
| 8576 |
+
# uncertainty, not evidence. Deterministic skeleton/fact/entity
|
| 8577 |
+
# validators remain authoritative and already ran.
|
| 8578 |
+
self._record_translation_assurance(
|
| 8579 |
+
'TRANSLATION_QA_NO_TYPED_BLOCKER', target_lang=target_lang, block_id=row_id,
|
| 8580 |
+
severity='diagnostic', decision='PASS_WITH_WARNING',
|
| 8581 |
+
detail={'semantic_object_version': self.TRANSLATION_SEMANTIC_OBJECT_VERSION},
|
| 8582 |
+
)
|
| 8583 |
+
continue
|
| 8584 |
issues.setdefault(meta['block_id'], {})[meta['claim_id']] = list(dict.fromkeys(codes))[:5]
|
| 8585 |
return issues
|
| 8586 |
|
|
|
|
| 8944 |
'grammar_evidence_codes': sorted({str(code or '').strip().upper() for code in (evidence.get('grammar_evidence_codes') or []) if str(code or '').strip()}),
|
| 8945 |
'contamination_evidence_codes': sorted({str(code or '').strip().upper() for code in (evidence.get('contamination_evidence_codes') or []) if str(code or '').strip()}),
|
| 8946 |
'detected_unprotected_english_tokens': sorted({str(token or '').strip().casefold() for token in (evidence.get('detected_unprotected_english_tokens') or []) if str(token or '').strip()}),
|
| 8947 |
+
'semantic_object_version': str(evidence.get('semantic_object_version') or ''),
|
| 8948 |
+
'semantic_object_delta_ids': sorted({
|
| 8949 |
+
str(item.get('object_id') or item.get('dimension') or '').strip()
|
| 8950 |
+
for item in (evidence.get('semantic_object_deltas') or []) if isinstance(item, dict)
|
| 8951 |
+
and str(item.get('object_id') or item.get('dimension') or '').strip()
|
| 8952 |
+
}),
|
| 8953 |
}
|
| 8954 |
payload = json.dumps(bounded, ensure_ascii=False, sort_keys=True, separators=(',', ':'))
|
| 8955 |
return hashlib.sha256(payload.encode('utf-8')).hexdigest()
|
|
|
|
| 9010 |
strategy = str(repair_strategy or 'LOCAL_EDIT').strip().upper()
|
| 9011 |
if strategy not in {'LOCAL_EDIT', 'SOURCE_REGENERATE'}:
|
| 9012 |
strategy = 'LOCAL_EDIT'
|
| 9013 |
+
evidence_payload = {
|
| 9014 |
+
'issue_codes': codes[:5], 'repair_strategy': strategy,
|
| 9015 |
+
'semantic_object': self._translation_semantic_object_frame(
|
| 9016 |
+
source_claim, getattr(self, '_translation_section_name', '')
|
| 9017 |
+
),
|
| 9018 |
+
}
|
| 9019 |
if strategy == 'LOCAL_EDIT':
|
| 9020 |
evidence_payload['current_target_claim'] = str(current_target_claim or '').strip()
|
| 9021 |
lexical_tokens = [
|
|
|
|
| 9030 |
]
|
| 9031 |
if semantic_delta_codes:
|
| 9032 |
evidence_payload['semantic_delta_codes'] = semantic_delta_codes[:6]
|
| 9033 |
+
semantic_object_deltas = [
|
| 9034 |
+
dict(item) for item in (supplied_evidence.get('semantic_object_deltas') or [])
|
| 9035 |
+
if isinstance(item, dict) and str(item.get('dimension') or '').strip()
|
| 9036 |
+
]
|
| 9037 |
+
if semantic_object_deltas:
|
| 9038 |
+
evidence_payload['semantic_object_deltas'] = semantic_object_deltas[:6]
|
| 9039 |
grammar_evidence_codes = [
|
| 9040 |
str(code or '').strip().upper() for code in (supplied_evidence.get('grammar_evidence_codes') or [])
|
| 9041 |
if str(code or '').strip()
|
|
|
|
| 9060 |
+ '. Use the bounded detector evidence below only to repair the supplied claim; do not invent additional defects. '
|
| 9061 |
'For LOCAL_EDIT, the current target claim is context, not authority; for SOURCE_REGENERATE it is intentionally absent. The frozen English source is always authoritative. '
|
| 9062 |
'If detected_unprotected_english_tokens is non-empty, every listed token is deterministic lexical-purity evidence from a prior target and MUST NOT survive as ordinary untranslated target-language prose unless represented by a protected SP-T anchor. '
|
| 9063 |
+
'If semantic_delta_codes is non-empty, enforce ONLY those evidenced semantic dimensions against the frozen English source and preserve all unaffected actor, relation, object, polarity, modality, condition, comparison, temporal scope, and evidentiary strength. When semantic_object_deltas is present, its source_span/target_span pairs are the narrow object-bound evidence; repair that object relation only and do not reinterpret the rest of the claim. UNRESOLVED_SEMANTIC_DELTA is a signal to regenerate faithfully from source, not an instruction to guess a specific correction. '
|
| 9064 |
'If grammar_evidence_codes is non-empty, repair or avoid only those bounded grammatical defect classes; GRAMMAR_UNSPECIFIED requires a fresh natural target-language rendering rather than blind micro-editing. '
|
| 9065 |
'If contamination_evidence_codes identifies KK_RUSSIAN_LEXICAL_INTRUSION or KK_RUSSIAN_SYNTACTIC_INTRUSION, remove only the evidenced Russian substitution/calque while preserving the frozen English meaning; do not invent an English lexical defect when deterministic English-token evidence is absent. '
|
| 9066 |
'Preserve every SP-P, SP-C, and SP-T marker byte-for-byte. Do not add claims or factual content. Return only the marked translated claim.'
|
|
|
|
| 9171 |
'source_hash': hashlib.sha256(str(item['text']).encode('utf-8')).hexdigest(),
|
| 9172 |
'semantic_frame': cls._source_semantic_claim_frame(item['text'], section_name),
|
| 9173 |
'semantic_frame_hash': cls._source_semantic_claim_frame(item['text'], section_name).get('frame_hash', ''),
|
| 9174 |
+
'semantic_object': cls._translation_semantic_object_frame(item['text'], section_name),
|
| 9175 |
+
'semantic_object_hash': cls._translation_semantic_object_frame(item['text'], section_name).get('object_hash', ''),
|
| 9176 |
+
'semantic_object_version': cls.TRANSLATION_SEMANTIC_OBJECT_VERSION,
|
| 9177 |
'target_text': target_text,
|
| 9178 |
'qa_status': 'repair_pending' if codes else 'passed',
|
| 9179 |
'issue_codes': [str(code or '').strip().upper() for code in codes if str(code or '').strip()][:5],
|
|
|
|
| 9218 |
row = states.get(key)
|
| 9219 |
expected_hash = hashlib.sha256(str(item['text']).encode('utf-8')).hexdigest()
|
| 9220 |
expected_semantic_hash = cls._source_semantic_claim_frame(item['text'], section_name).get('frame_hash', '')
|
| 9221 |
+
expected_object_hash = cls._translation_semantic_object_frame(item['text'], section_name).get('object_hash', '')
|
| 9222 |
if not isinstance(row, dict) or row.get('source_hash') != expected_hash or not str(row.get('target_text') or '').strip():
|
| 9223 |
return {}, {}, False
|
| 9224 |
+
# R6-R9: durable target claims are semantic-object bound. Any
|
| 9225 |
+
# pre-v1 object state is target-only stale and must be rebuilt;
|
| 9226 |
+
# sealed English remains untouched.
|
| 9227 |
+
if str(row.get('semantic_object_version') or '') != cls.TRANSLATION_SEMANTIC_OBJECT_VERSION:
|
| 9228 |
+
return {}, {}, False
|
| 9229 |
+
if str(row.get('semantic_object_hash') or '') != expected_object_hash:
|
| 9230 |
+
return {}, {}, False
|
| 9231 |
stored_semantic_hash = str(row.get('semantic_frame_hash') or '')
|
| 9232 |
if stored_semantic_hash and stored_semantic_hash != expected_semantic_hash:
|
| 9233 |
return {}, {}, False
|
|
|
|
| 9342 |
for item in (repair_evidence.get('grammar_evidence_codes') or [])
|
| 9343 |
if str(item or '').strip()
|
| 9344 |
] or ['GRAMMAR_UNSPECIFIED']
|
| 9345 |
+
semantic_evidence_codes = [
|
| 9346 |
+
str(item or '').strip().upper()
|
| 9347 |
+
for item in (repair_evidence.get('semantic_delta_codes') or [])
|
| 9348 |
+
if str(item or '').strip()
|
| 9349 |
+
]
|
| 9350 |
+
semantic_object_deltas = [
|
| 9351 |
+
dict(item) for item in (repair_evidence.get('semantic_object_deltas') or [])
|
| 9352 |
+
if isinstance(item, dict)
|
| 9353 |
+
]
|
| 9354 |
rows.append({
|
| 9355 |
'id': f'SP-P{block_id}-{claim_id}', 'block_id': block_id,
|
| 9356 |
'claim_id': claim_id, 'code': code, 'english': source_claims[claim_id],
|
| 9357 |
'target': target_claims[claim_id], 'deterministic_confirmed': False,
|
| 9358 |
'grammar_evidence_codes': grammar_evidence_codes[:4],
|
| 9359 |
+
'semantic_evidence_codes': semantic_evidence_codes[:6],
|
| 9360 |
+
'semantic_object_deltas': semantic_object_deltas[:6],
|
| 9361 |
+
'semantic_object': self._translation_semantic_object_frame(
|
| 9362 |
+
source_claims[claim_id], section_name, glossary
|
| 9363 |
+
),
|
| 9364 |
})
|
| 9365 |
|
| 9366 |
confirmed = {}
|
|
|
|
| 9381 |
return confirmed
|
| 9382 |
|
| 9383 |
allowed_evidence = {
|
| 9384 |
+
'CLAIM_MISSING','CLAIM_ADDED','ACTOR_CHANGED','RELATION_CHANGED','OBJECT_CHANGED',
|
| 9385 |
+
'CONDITION_SCOPE_CHANGED','COMPARISON_DIRECTION_CHANGED','TEMPORAL_SCOPE_CHANGED',
|
| 9386 |
+
'POLARITY_CHANGED','CAUSALITY_STRENGTH_CHANGED','MODALITY_UNCERTAINTY_CHANGED',
|
| 9387 |
+
'CITATION_MOVED','ACRONYM_MISATTACHED',
|
| 9388 |
'GRAMMATICAL_VIOLATION','CASE_GOVERNMENT','AGREEMENT','MORPHOLOGY','VERB_FORM',
|
| 9389 |
'SYNTAX_STRUCTURE','PUNCTUATION','SYNTACTIC_COMPLETENESS','GRAMMAR_UNSPECIFIED',
|
| 9390 |
'WORD_ORDER_UNGRAMMATICAL','WORD_ORDER_SEMANTIC_SCOPE_CHANGED',
|
|
|
|
| 9409 |
},
|
| 9410 |
'contamination_evidence_codes': list(row.get('contamination_evidence_codes') or [])[:2],
|
| 9411 |
'grammar_evidence_codes': list(row.get('grammar_evidence_codes') or [])[:4],
|
| 9412 |
+
'semantic_evidence_codes': list(row.get('semantic_evidence_codes') or [])[:6],
|
| 9413 |
+
'semantic_object_deltas': list(row.get('semantic_object_deltas') or [])[:6],
|
| 9414 |
+
'semantic_object': dict(row.get('semantic_object') or {}),
|
| 9415 |
} for row in model_rows],
|
| 9416 |
}
|
| 9417 |
lexical_contract = self._translation_lexical_retention_contract(target_lang)
|
|
|
|
| 9420 |
{lexical_contract}
|
| 9421 |
For Russian, allow required case/number/gender inflection and retain sustained non-Russian prose as an additional contamination signal. For Kazakh, allow productive case, possessive, person/plural and vowel-harmony suffixes on the correct term stem, but treat clearly Russian lexical/syntactic substitution as blocking only when the pair itself supports that conclusion. For Chinese, allow natural reordering and sentence reshaping inside the same claim while preserving meaning.
|
| 9422 |
For LANGUAGE_CONTAMINATION, deterministic_lexical is authoritative for ordinary English/Latin residue: when confirmed=false and token_count=0, you MUST NOT confirm UNTRANSLATED_LEXEME or UNTRANSLATED_PROSE. For Kazakh, a non-Latin contamination blocker may be confirmed only when contamination_evidence_codes contains KK_RUSSIAN_LEXICAL_INTRUSION or KK_RUSSIAN_SYNTACTIC_INTRUSION, and the evidence_code must match one of those supplied typed codes. The per-item glossary lists the frozen target terms that are legal for that source claim.
|
| 9423 |
+
For semantic issues, semantic_object is immutable source authority and semantic_evidence_codes/semantic_object_deltas are the only admissible semantic defect lineage. A generic claim that meaning changed is NOT sufficient. For SEMANTIC_DRIFT, confirm blocking only when the evidence_code exactly matches one of the supplied object-bound semantic dimensions. If the prior semantic object evidence is absent, invalid, or the current pair no longer supports the same dimension, return NOT_CONFIRMED. For NEGATION_DRIFT, CAUSALITY_STRENGTH_DRIFT, UNCERTAINTY_DRIFT, OMITTED_CLAIM, and ADDED_CLAIM, evidence_code must remain continuous with the corresponding supplied semantic_evidence_codes.
|
| 9424 |
For GRAMMAR, grammar_evidence_codes is the typed defect history and must remain continuous into this final decision. Never use generic GRAMMATICAL_VIOLATION as final blocking evidence when a typed grammar history exists. If WORD_ORDER is supplied, non-canonical or stylistically different ordering is NOT blocking. Confirm only when the target itself is ungrammatical because of ordering (WORD_ORDER_UNGRAMMATICAL) or when ordering changes modifier/negation/modality/condition/relation scope relative to the English claim (WORD_ORDER_SEMANTIC_SCOPE_CHANGED). For CASE_GOVERNMENT, AGREEMENT, MORPHOLOGY, VERB_FORM, SYNTAX_STRUCTURE, PUNCTUATION, or SYNTACTIC_COMPLETENESS, a blocking evidence_code must match the supplied typed class. If only GRAMMAR_UNSPECIFIED was supplied, you may narrow it to one of those specific classes or one of the two WORD_ORDER consequence codes; do not confirm a generic grammar label.
|
| 9425 |
Return valid JSON only: {{"items":[{{"id":"SP-P000001-C000001","issue_code":"GRAMMAR","decision":"CONFIRMED_BLOCKING","evidence_code":"AGREEMENT"}}]}}.
|
| 9426 |
Decision must be CONFIRMED_BLOCKING or NOT_CONFIRMED. evidence_code must be one of: {sorted(allowed_evidence)}. Return every supplied id+issue_code pair exactly once."""
|
|
|
|
| 9449 |
evidence_code = str(item.get('evidence_code') or '').strip().upper()
|
| 9450 |
if decision not in {'CONFIRMED_BLOCKING', 'NOT_CONFIRMED'} or evidence_code not in allowed_evidence:
|
| 9451 |
raise DeepSeekRequestError(f'Translation QA adjudication returned invalid bounded decision for {row["id"]}.')
|
| 9452 |
+
if row['code'] in {
|
| 9453 |
+
'SEMANTIC_DRIFT','NEGATION_DRIFT','CAUSALITY_STRENGTH_DRIFT',
|
| 9454 |
+
'UNCERTAINTY_DRIFT','OMITTED_CLAIM','ADDED_CLAIM',
|
| 9455 |
+
} and decision == 'CONFIRMED_BLOCKING':
|
| 9456 |
+
typed = {
|
| 9457 |
+
str(code or '').strip().upper()
|
| 9458 |
+
for code in (row.get('semantic_evidence_codes') or []) if str(code or '').strip()
|
| 9459 |
+
}
|
| 9460 |
+
if not typed or evidence_code not in typed:
|
| 9461 |
+
decision = 'NOT_CONFIRMED'
|
| 9462 |
+
evidence_code = 'QA_DISAGREEMENT'
|
| 9463 |
+
elif row['code'] == 'SEMANTIC_DRIFT':
|
| 9464 |
+
object_dims = {
|
| 9465 |
+
str(item.get('dimension') or '').strip().upper()
|
| 9466 |
+
for item in (row.get('semantic_object_deltas') or []) if isinstance(item, dict)
|
| 9467 |
+
}
|
| 9468 |
+
if evidence_code not in object_dims:
|
| 9469 |
+
decision = 'NOT_CONFIRMED'
|
| 9470 |
+
evidence_code = 'QA_DISAGREEMENT'
|
| 9471 |
if row['code'] == 'LANGUAGE_CONTAMINATION':
|
| 9472 |
lexical = dict(row.get('lexical_evidence') or {})
|
| 9473 |
typed = set(row.get('contamination_evidence_codes') or [])
|
|
|
|
| 9847 |
semantic_deltas.append(fallback)
|
| 9848 |
if semantic_deltas:
|
| 9849 |
repair_evidence['semantic_delta_codes'] = list(dict.fromkeys(semantic_deltas))[:6]
|
| 9850 |
+
specific_semantic = list(dict.fromkeys(semantic_deltas))[:6]
|
| 9851 |
+
# R6-R9: generic semantic drift cannot arrive here without
|
| 9852 |
+
# a source/target-bound semantic object. Specific semantic
|
| 9853 |
+
# codes (polarity/causality/modality/claim add-missing) are
|
| 9854 |
+
# derived directly above and do not require a second free
|
| 9855 |
+
# semantic-decomposition model call.
|
| 9856 |
+
if semantic_issue and not specific_semantic:
|
| 9857 |
+
repair_evidence['semantic_delta_codes'] = ['UNRESOLVED_SEMANTIC_DELTA']
|
| 9858 |
if repair_evidence.get('semantic_delta_codes'):
|
| 9859 |
self._record_translation_assurance(
|
| 9860 |
'SEMANTIC_REPAIR_EVIDENCE', target_lang=target_lang,
|