Spaces:
Running
Running
R6A-R4-R8 front matter JSON output and bounded schema retry
#44
by GuXiaopeng - opened
core/pipeline_agent.py
CHANGED
|
@@ -587,6 +587,7 @@ class SSciPipelineAgent:
|
|
| 587 |
temp,
|
| 588 |
stream,
|
| 589 |
reasoning_effort,
|
|
|
|
| 590 |
):
|
| 591 |
payload = {
|
| 592 |
"model": model_name,
|
|
@@ -609,6 +610,9 @@ class SSciPipelineAgent:
|
|
| 609 |
min(float(temp), 2.0),
|
| 610 |
)
|
| 611 |
|
|
|
|
|
|
|
|
|
|
| 612 |
return payload
|
| 613 |
|
| 614 |
@staticmethod
|
|
@@ -729,6 +733,7 @@ class SSciPipelineAgent:
|
|
| 729 |
thinking=None,
|
| 730 |
reasoning_effort=None,
|
| 731 |
allow_fallback=None,
|
|
|
|
| 732 |
):
|
| 733 |
(
|
| 734 |
requested_model,
|
|
@@ -779,6 +784,7 @@ class SSciPipelineAgent:
|
|
| 779 |
temp=temp,
|
| 780 |
stream=False,
|
| 781 |
reasoning_effort=resolved_effort,
|
|
|
|
| 782 |
)
|
| 783 |
start_time = time.time()
|
| 784 |
|
|
@@ -1384,6 +1390,58 @@ Return a concise PROVISIONAL RESEARCH LOGIC CONTRACT in plain English prose. It
|
|
| 1384 |
)
|
| 1385 |
return text
|
| 1386 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1387 |
def generate_working_front_matter(
|
| 1388 |
self, topic, keywords, research_logic_contract, synthesis_result, data_evidence='', mode='amj'
|
| 1389 |
):
|
|
@@ -1406,16 +1464,15 @@ USER KEYWORD SEEDS: {keywords}
|
|
| 1406 |
<FROZEN_RESEARCH_EVIDENCE>
|
| 1407 |
{evidence}
|
| 1408 |
</FROZEN_RESEARCH_EVIDENCE>"""
|
| 1409 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1410 |
'You are a senior management-journal editor creating provisional front matter without anticipating results.',
|
| 1411 |
-
prompt,
|
| 1412 |
)
|
| 1413 |
-
parsed = self._extract_json_from_text(response)
|
| 1414 |
-
if not isinstance(parsed, dict):
|
| 1415 |
-
raise DeepSeekRequestError('Working front matter was not valid JSON.')
|
| 1416 |
-
abstract = self._validate_working_abstract(parsed.get('abstract'))
|
| 1417 |
-
working_keywords = self._front_matter_keywords(parsed.get('keywords'))
|
| 1418 |
-
return {'abstract': abstract, 'keywords': working_keywords}
|
| 1419 |
|
| 1420 |
def generate_final_front_matter(self, topic, keywords, manuscript, data_evidence='', mode='amj'):
|
| 1421 |
"""Generate final Abstract + Keywords only from the frozen completed body/evidence."""
|
|
@@ -1433,16 +1490,18 @@ USER KEYWORD SEEDS: {keywords}
|
|
| 1433 |
<COMPLETED_FROZEN_MANUSCRIPT>
|
| 1434 |
{body}
|
| 1435 |
</COMPLETED_FROZEN_MANUSCRIPT>"""
|
| 1436 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1437 |
'You are a senior journal editor finalizing front matter from frozen manuscript evidence.',
|
| 1438 |
-
prompt,
|
| 1439 |
)
|
| 1440 |
-
|
| 1441 |
-
if not isinstance(parsed, dict):
|
| 1442 |
-
raise DeepSeekRequestError('Final front matter was not valid JSON.')
|
| 1443 |
-
abstract = re.sub(r'(?im)^#+\s*abstract\s*', '', str(parsed.get('abstract') or '')).strip()
|
| 1444 |
-
if self._front_matter_has_citation(abstract):
|
| 1445 |
-
raise DeepSeekRequestError('Final Abstract must not contain citations.')
|
| 1446 |
abstract = self.polish_academic_section(abstract, 'Abstract', abstract=True, mode=mode)
|
| 1447 |
if self._front_matter_has_citation(abstract):
|
| 1448 |
raise DeepSeekRequestError('Final Abstract revision introduced a citation.')
|
|
@@ -1455,8 +1514,7 @@ USER KEYWORD SEEDS: {keywords}
|
|
| 1455 |
'Final Abstract length diagnostic | words=%s | preferred=80-%s | nonfatal=true',
|
| 1456 |
final_word_count, max_words,
|
| 1457 |
)
|
| 1458 |
-
|
| 1459 |
-
return {'abstract': abstract, 'keywords': final_keywords}
|
| 1460 |
|
| 1461 |
# Backward-compatible name retained for old checkpoints/tests.
|
| 1462 |
def stream_generate_anchors(self, topic, keywords, synthesis_result, refs_text, data_evidence="", mode="amj"):
|
|
|
|
| 587 |
temp,
|
| 588 |
stream,
|
| 589 |
reasoning_effort,
|
| 590 |
+
response_format=None,
|
| 591 |
):
|
| 592 |
payload = {
|
| 593 |
"model": model_name,
|
|
|
|
| 610 |
min(float(temp), 2.0),
|
| 611 |
)
|
| 612 |
|
| 613 |
+
if response_format is not None:
|
| 614 |
+
payload["response_format"] = dict(response_format)
|
| 615 |
+
|
| 616 |
return payload
|
| 617 |
|
| 618 |
@staticmethod
|
|
|
|
| 733 |
thinking=None,
|
| 734 |
reasoning_effort=None,
|
| 735 |
allow_fallback=None,
|
| 736 |
+
response_format=None,
|
| 737 |
):
|
| 738 |
(
|
| 739 |
requested_model,
|
|
|
|
| 784 |
temp=temp,
|
| 785 |
stream=False,
|
| 786 |
reasoning_effort=resolved_effort,
|
| 787 |
+
response_format=response_format,
|
| 788 |
)
|
| 789 |
start_time = time.time()
|
| 790 |
|
|
|
|
| 1390 |
)
|
| 1391 |
return text
|
| 1392 |
|
| 1393 |
+
def _generate_structured_front_matter(
|
| 1394 |
+
self,
|
| 1395 |
+
system_prompt,
|
| 1396 |
+
user_prompt,
|
| 1397 |
+
*,
|
| 1398 |
+
phase,
|
| 1399 |
+
max_tokens,
|
| 1400 |
+
validator,
|
| 1401 |
+
):
|
| 1402 |
+
"""Generate one front-matter object with one bounded content-only retry."""
|
| 1403 |
+
base_prompt = str(user_prompt)
|
| 1404 |
+
current_prompt = base_prompt
|
| 1405 |
+
last_error = None
|
| 1406 |
+
for local_attempt in range(2):
|
| 1407 |
+
# API/auth/balance/cancellation/transport behavior remains owned by
|
| 1408 |
+
# call_deepseek(). Those failures are deliberately not caught here.
|
| 1409 |
+
response = self.call_deepseek(
|
| 1410 |
+
system_prompt,
|
| 1411 |
+
current_prompt,
|
| 1412 |
+
max_tokens=max_tokens,
|
| 1413 |
+
temp=0.0,
|
| 1414 |
+
model_role='writing',
|
| 1415 |
+
thinking=False,
|
| 1416 |
+
allow_fallback=False,
|
| 1417 |
+
response_format={"type": "json_object"},
|
| 1418 |
+
)
|
| 1419 |
+
try:
|
| 1420 |
+
parsed = self._extract_json_from_text(response)
|
| 1421 |
+
if not isinstance(parsed, dict):
|
| 1422 |
+
raise DeepSeekRequestError(f'{phase} front matter was not valid JSON.')
|
| 1423 |
+
if not isinstance(parsed.get('abstract'), str) or not str(parsed.get('abstract') or '').strip():
|
| 1424 |
+
raise DeepSeekRequestError(f'{phase} front matter abstract must be a non-empty string.')
|
| 1425 |
+
raw_keywords = parsed.get('keywords')
|
| 1426 |
+
if not isinstance(raw_keywords, list) or not all(isinstance(item, str) for item in raw_keywords):
|
| 1427 |
+
raise DeepSeekRequestError(f'{phase} front matter keywords must be a JSON array of strings.')
|
| 1428 |
+
return validator(parsed)
|
| 1429 |
+
except DeepSeekRequestError as exc:
|
| 1430 |
+
last_error = exc
|
| 1431 |
+
if local_attempt >= 1:
|
| 1432 |
+
raise
|
| 1433 |
+
logging.warning(
|
| 1434 |
+
'Front matter structured-output local retry | phase=%s | attempt=%s/2 | reason=%s',
|
| 1435 |
+
phase, local_attempt + 1, self._safe_source_error(exc),
|
| 1436 |
+
)
|
| 1437 |
+
current_prompt = base_prompt + (
|
| 1438 |
+
'\n\nLOCAL VALIDATION RETRY: The previous response failed local JSON/schema/content validation. '
|
| 1439 |
+
'Return a fresh complete JSON object only, with exactly a non-empty string field "abstract" '
|
| 1440 |
+
'and a JSON array field "keywords" containing 4-6 distinct non-empty academic terms. '
|
| 1441 |
+
'Do not describe the correction and do not wrap the JSON in Markdown.'
|
| 1442 |
+
)
|
| 1443 |
+
raise DeepSeekRequestError(f'{phase} front matter validation failed: {last_error}')
|
| 1444 |
+
|
| 1445 |
def generate_working_front_matter(
|
| 1446 |
self, topic, keywords, research_logic_contract, synthesis_result, data_evidence='', mode='amj'
|
| 1447 |
):
|
|
|
|
| 1464 |
<FROZEN_RESEARCH_EVIDENCE>
|
| 1465 |
{evidence}
|
| 1466 |
</FROZEN_RESEARCH_EVIDENCE>"""
|
| 1467 |
+
def validate_working(parsed):
|
| 1468 |
+
abstract = self._validate_working_abstract(parsed.get('abstract'))
|
| 1469 |
+
working_keywords = self._front_matter_keywords(parsed.get('keywords'))
|
| 1470 |
+
return {'abstract': abstract, 'keywords': working_keywords}
|
| 1471 |
+
|
| 1472 |
+
return self._generate_structured_front_matter(
|
| 1473 |
'You are a senior management-journal editor creating provisional front matter without anticipating results.',
|
| 1474 |
+
prompt, phase='Working', max_tokens=1100, validator=validate_working,
|
| 1475 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1476 |
|
| 1477 |
def generate_final_front_matter(self, topic, keywords, manuscript, data_evidence='', mode='amj'):
|
| 1478 |
"""Generate final Abstract + Keywords only from the frozen completed body/evidence."""
|
|
|
|
| 1490 |
<COMPLETED_FROZEN_MANUSCRIPT>
|
| 1491 |
{body}
|
| 1492 |
</COMPLETED_FROZEN_MANUSCRIPT>"""
|
| 1493 |
+
def validate_final(parsed):
|
| 1494 |
+
abstract = re.sub(r'(?im)^#+\s*abstract\s*', '', str(parsed.get('abstract') or '')).strip()
|
| 1495 |
+
if self._front_matter_has_citation(abstract):
|
| 1496 |
+
raise DeepSeekRequestError('Final Abstract must not contain citations.')
|
| 1497 |
+
final_keywords = self._front_matter_keywords(parsed.get('keywords'))
|
| 1498 |
+
return {'abstract': abstract, 'keywords': final_keywords}
|
| 1499 |
+
|
| 1500 |
+
structured = self._generate_structured_front_matter(
|
| 1501 |
'You are a senior journal editor finalizing front matter from frozen manuscript evidence.',
|
| 1502 |
+
prompt, phase='Final', max_tokens=1200, validator=validate_final,
|
| 1503 |
)
|
| 1504 |
+
abstract = structured['abstract']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1505 |
abstract = self.polish_academic_section(abstract, 'Abstract', abstract=True, mode=mode)
|
| 1506 |
if self._front_matter_has_citation(abstract):
|
| 1507 |
raise DeepSeekRequestError('Final Abstract revision introduced a citation.')
|
|
|
|
| 1514 |
'Final Abstract length diagnostic | words=%s | preferred=80-%s | nonfatal=true',
|
| 1515 |
final_word_count, max_words,
|
| 1516 |
)
|
| 1517 |
+
return {'abstract': abstract, 'keywords': structured['keywords']}
|
|
|
|
| 1518 |
|
| 1519 |
# Backward-compatible name retained for old checkpoints/tests.
|
| 1520 |
def stream_generate_anchors(self, topic, keywords, synthesis_result, refs_text, data_evidence="", mode="amj"):
|
static/previews/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"task_id": "task_r6a_r4_r4_progress", "request_hash": "e954898a5de4d01e361ec143cb36e8d46838844310afe6e74a5b9448335eb673", "execution_epoch": 1, "stage": "", "text": "", "complete": false, "target_stage": "translation_qa:Findings", "target_text": "# Кіріспе\n\nДайын.", "target_complete": false, "target_english_commit_text": "", "target_progress": {"qa_passed": 0, "repair_pending": 1, "queued": 2, "in_flight": 1, "total": 3}, "updated_at": "2026-08-17T05:08:49.171257+00:00"}
|