Spaces:
Running
Running
A5-R3-R4C dormant ModelPolicy v2 execution seam
#113
by GuXiaopeng - opened
- core/generation_runner.py +55 -5
- core/meta_extractor.py +3 -2
- core/model_gateway.py +170 -26
- core/pipeline_agent.py +86 -19
- core/qualitative_engine.py +2 -0
- core/quantitative_engine.py +1 -0
core/generation_runner.py
CHANGED
|
@@ -2888,11 +2888,53 @@ class GenerationRunner:
|
|
| 2888 |
routes = model.get("routes") if isinstance(model.get("routes"), dict) else {}
|
| 2889 |
if not routes:
|
| 2890 |
raise RuntimeError("trusted_task_policy_model_routes_missing")
|
| 2891 |
-
|
| 2892 |
-
|
| 2893 |
-
|
| 2894 |
-
|
| 2895 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2896 |
|
| 2897 |
def _run_impl(self) -> dict[str, Any]:
|
| 2898 |
req = self.request
|
|
@@ -2921,6 +2963,8 @@ class GenerationRunner:
|
|
| 2921 |
agent = SSciPipelineAgent(ds_key, s2_key)
|
| 2922 |
self.agent = agent
|
| 2923 |
agent.set_model_policy_snapshot(plan_policy_snapshot)
|
|
|
|
|
|
|
| 2924 |
agent.set_cancel_checker(self.cancel_checker)
|
| 2925 |
# DeepSeek/search activity marks the executor as genuinely alive; the
|
| 2926 |
# heartbeat thread decides separately when to renew D1's lease.
|
|
@@ -3661,6 +3705,8 @@ class GenerationRunner:
|
|
| 3661 |
# glossary order deterministic and avoids sharing mutable writer state.
|
| 3662 |
translation_agent = SSciPipelineAgent(ds_key, s2_key)
|
| 3663 |
translation_agent.set_model_policy_snapshot(plan_policy_snapshot)
|
|
|
|
|
|
|
| 3664 |
self.translation_agent = translation_agent
|
| 3665 |
translation_agent.set_execution_progress_hook(lambda: self._progress_activity())
|
| 3666 |
translation_agent.set_cancel_checker(lambda: self.cancel_checker())
|
|
@@ -3902,6 +3948,9 @@ class GenerationRunner:
|
|
| 3902 |
for word in ["abstract", "keyword", "conclusion", "references", "appendix"]
|
| 3903 |
)
|
| 3904 |
section_id = str(sec.get("section_id") or "")
|
|
|
|
|
|
|
|
|
|
| 3905 |
planned_items = [
|
| 3906 |
item for item in list(citation_plan.get(display_name, []))
|
| 3907 |
if agent.citation_source_role_eligible(
|
|
@@ -4224,6 +4273,7 @@ The previous attempt for this same manuscript unit was rejected by validation. R
|
|
| 4224 |
agent.stream_deepseek(
|
| 4225 |
sys_prompt, candidate_user_prompt, max_tokens=7000,
|
| 4226 |
model_role="writing", thinking=False, allow_fallback=False,
|
|
|
|
| 4227 |
),
|
| 4228 |
preview_prefix=full_draft_en + prefix,
|
| 4229 |
preview_stage=(display_name if section_attempt == 1 else f"{display_name}:retry"),
|
|
|
|
| 2888 |
routes = model.get("routes") if isinstance(model.get("routes"), dict) else {}
|
| 2889 |
if not routes:
|
| 2890 |
raise RuntimeError("trusted_task_policy_model_routes_missing")
|
| 2891 |
+
if str(snapshot.get("model_policy_version") or "") not in {
|
| 2892 |
+
"model-standard-v2", "model-flagship-v2"
|
| 2893 |
+
}:
|
| 2894 |
+
# Preserve the dormant R3A v1 fail-closed contract; only v2 may
|
| 2895 |
+
# cross the new trusted logical-node execution seam.
|
| 2896 |
+
raise RuntimeError("trusted_task_policy_model_route_contract_not_ready")
|
| 2897 |
+
if str(model.get("provider_catalog_version") or "") != "deepseek-v4-concrete-v1":
|
| 2898 |
+
raise RuntimeError("trusted_task_policy_provider_catalog_invalid")
|
| 2899 |
+
return snapshot
|
| 2900 |
+
|
| 2901 |
+
@staticmethod
|
| 2902 |
+
def _section_logical_node(mode: str, section_id: str, subsection_id: str | None = None) -> str | None:
|
| 2903 |
+
"""Map academic topology to a tier-neutral writer logical node."""
|
| 2904 |
+
mode = str(mode or "").strip().lower()
|
| 2905 |
+
section_id = str(section_id or "").strip().lower()
|
| 2906 |
+
subsection_id = str(subsection_id or "").strip().lower()
|
| 2907 |
+
if mode == "amj":
|
| 2908 |
+
if section_id in {"introduction", "theory"}:
|
| 2909 |
+
return "core_section_writing"
|
| 2910 |
+
if section_id == "methodology":
|
| 2911 |
+
return "methods_writing"
|
| 2912 |
+
if section_id == "results":
|
| 2913 |
+
return "quantitative_results_writing"
|
| 2914 |
+
if section_id == "discussion":
|
| 2915 |
+
if subsection_id in {"practical_implications", "limitations"}:
|
| 2916 |
+
return "ordinary_section_writing"
|
| 2917 |
+
return "core_section_writing"
|
| 2918 |
+
if section_id == "conclusion":
|
| 2919 |
+
return "ordinary_section_writing"
|
| 2920 |
+
elif mode == "asq":
|
| 2921 |
+
if section_id in {"introduction", "theoretical_framing"}:
|
| 2922 |
+
return "core_section_writing"
|
| 2923 |
+
if section_id == "methods":
|
| 2924 |
+
return "methods_writing"
|
| 2925 |
+
if section_id == "findings":
|
| 2926 |
+
if subsection_id == "emergent_model":
|
| 2927 |
+
return "core_section_writing"
|
| 2928 |
+
return "evidence_rich_findings_narration"
|
| 2929 |
+
if section_id == "discussion":
|
| 2930 |
+
if subsection_id in {"boundaries", "practical_implications"}:
|
| 2931 |
+
return "ordinary_section_writing"
|
| 2932 |
+
return "core_section_writing"
|
| 2933 |
+
if section_id == "conclusion":
|
| 2934 |
+
return "ordinary_section_writing"
|
| 2935 |
+
if section_id == "appendix":
|
| 2936 |
+
return None
|
| 2937 |
+
raise RuntimeError(f"logical_node_mapping_missing:{mode}:{section_id}:{subsection_id}")
|
| 2938 |
|
| 2939 |
def _run_impl(self) -> dict[str, Any]:
|
| 2940 |
req = self.request
|
|
|
|
| 2963 |
agent = SSciPipelineAgent(ds_key, s2_key)
|
| 2964 |
self.agent = agent
|
| 2965 |
agent.set_model_policy_snapshot(plan_policy_snapshot)
|
| 2966 |
+
if trusted_task_policy_snapshot is not None:
|
| 2967 |
+
agent.set_trusted_product_policy_snapshot(trusted_task_policy_snapshot)
|
| 2968 |
agent.set_cancel_checker(self.cancel_checker)
|
| 2969 |
# DeepSeek/search activity marks the executor as genuinely alive; the
|
| 2970 |
# heartbeat thread decides separately when to renew D1's lease.
|
|
|
|
| 3705 |
# glossary order deterministic and avoids sharing mutable writer state.
|
| 3706 |
translation_agent = SSciPipelineAgent(ds_key, s2_key)
|
| 3707 |
translation_agent.set_model_policy_snapshot(plan_policy_snapshot)
|
| 3708 |
+
if trusted_task_policy_snapshot is not None:
|
| 3709 |
+
translation_agent.set_trusted_product_policy_snapshot(trusted_task_policy_snapshot)
|
| 3710 |
self.translation_agent = translation_agent
|
| 3711 |
translation_agent.set_execution_progress_hook(lambda: self._progress_activity())
|
| 3712 |
translation_agent.set_cancel_checker(lambda: self.cancel_checker())
|
|
|
|
| 3948 |
for word in ["abstract", "keyword", "conclusion", "references", "appendix"]
|
| 3949 |
)
|
| 3950 |
section_id = str(sec.get("section_id") or "")
|
| 3951 |
+
section_logical_node = self._section_logical_node(
|
| 3952 |
+
mode, section_id, sec.get("subsection_id")
|
| 3953 |
+
)
|
| 3954 |
planned_items = [
|
| 3955 |
item for item in list(citation_plan.get(display_name, []))
|
| 3956 |
if agent.citation_source_role_eligible(
|
|
|
|
| 4273 |
agent.stream_deepseek(
|
| 4274 |
sys_prompt, candidate_user_prompt, max_tokens=7000,
|
| 4275 |
model_role="writing", thinking=False, allow_fallback=False,
|
| 4276 |
+
logical_node=section_logical_node,
|
| 4277 |
),
|
| 4278 |
preview_prefix=full_draft_en + prefix,
|
| 4279 |
preview_stage=(display_name if section_attempt == 1 else f"{display_name}:retry"),
|
core/meta_extractor.py
CHANGED
|
@@ -21,7 +21,8 @@ class MetaExtractor:
|
|
| 21 |
prompt,
|
| 22 |
snippet,
|
| 23 |
max_tokens=1000,
|
| 24 |
-
model_name="deepseek-chat"
|
|
|
|
| 25 |
)
|
| 26 |
|
| 27 |
# 强制提取 JSON 部分,过滤模型可能的解释性前缀或后缀
|
|
@@ -49,4 +50,4 @@ class MetaExtractor:
|
|
| 49 |
|
| 50 |
def extract_golden_metadata(agent, paper_texts: List[str]):
|
| 51 |
extractor = MetaExtractor(agent)
|
| 52 |
-
return extractor.run_batch_extraction(paper_texts)
|
|
|
|
| 21 |
prompt,
|
| 22 |
snippet,
|
| 23 |
max_tokens=1000,
|
| 24 |
+
model_name="deepseek-chat",
|
| 25 |
+
logical_node='literature_parsing',
|
| 26 |
)
|
| 27 |
|
| 28 |
# 强制提取 JSON 部分,过滤模型可能的解释性前缀或后缀
|
|
|
|
| 50 |
|
| 51 |
def extract_golden_metadata(agent, paper_texts: List[str]):
|
| 52 |
extractor = MetaExtractor(agent)
|
| 53 |
+
return extractor.run_batch_extraction(paper_texts)
|
core/model_gateway.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
"""Provider-neutral model routing primitives for SmartPaper.
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
|
|
|
| 8 |
"""
|
| 9 |
from __future__ import annotations
|
| 10 |
|
|
@@ -40,14 +41,18 @@ class ModelRoute:
|
|
| 40 |
fallback_providers: tuple[str, ...] = ()
|
| 41 |
|
| 42 |
|
| 43 |
-
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
registered later without changing manuscript generation code.
|
| 50 |
-
"""
|
| 51 |
|
| 52 |
def __init__(self, config: dict[str, Any] | None = None):
|
| 53 |
cfg = dict(config or {})
|
|
@@ -61,6 +66,7 @@ class ProviderNeutralModelGateway:
|
|
| 61 |
if isinstance(row, str):
|
| 62 |
provider = str(row or self.default_provider).strip().lower()
|
| 63 |
provider_role = cap
|
|
|
|
| 64 |
else:
|
| 65 |
item = dict(row or {})
|
| 66 |
provider = str(item.get("provider") or self.default_provider).strip().lower()
|
|
@@ -73,12 +79,15 @@ class ProviderNeutralModelGateway:
|
|
| 73 |
for name in raw_fallbacks
|
| 74 |
if str(name or "").strip()
|
| 75 |
)
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
|
|
|
|
|
|
| 79 |
self._call_adapters: dict[str, Callable[..., str]] = {}
|
| 80 |
self._stream_adapters: dict[str, Callable[..., Iterable[str]]] = {}
|
| 81 |
self._policy_snapshot: dict[str, Any] = {}
|
|
|
|
| 82 |
|
| 83 |
def register_provider(
|
| 84 |
self,
|
|
@@ -93,6 +102,7 @@ class ProviderNeutralModelGateway:
|
|
| 93 |
self._call_adapters[name] = call_adapter
|
| 94 |
self._stream_adapters[name] = stream_adapter
|
| 95 |
|
|
|
|
| 96 |
def set_policy_snapshot(self, snapshot: dict[str, Any] | None) -> None:
|
| 97 |
self._policy_snapshot = deepcopy(dict(snapshot or {}))
|
| 98 |
|
|
@@ -100,6 +110,19 @@ class ProviderNeutralModelGateway:
|
|
| 100 |
def policy_snapshot(self) -> dict[str, Any]:
|
| 101 |
return deepcopy(self._policy_snapshot)
|
| 102 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
def _route(self, capability: str) -> ModelRoute:
|
| 104 |
cap = str(capability or "reasoning").strip().lower() or "reasoning"
|
| 105 |
policy_routes = dict(self._policy_snapshot.get("model_routes") or {})
|
|
@@ -114,7 +137,8 @@ class ProviderNeutralModelGateway:
|
|
| 114 |
provider=str(row.get("provider") or self.default_provider).strip().lower(),
|
| 115 |
provider_role=str(row.get("provider_role") or cap).strip().lower(),
|
| 116 |
fallback_providers=tuple(
|
| 117 |
-
str(name or "").strip().lower()
|
|
|
|
| 118 |
if str(name or "").strip()
|
| 119 |
),
|
| 120 |
)
|
|
@@ -126,7 +150,118 @@ class ProviderNeutralModelGateway:
|
|
| 126 |
name for name in (route.provider, *route.fallback_providers) if name
|
| 127 |
))
|
| 128 |
|
| 129 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
route = self._route(capability)
|
| 131 |
missing = []
|
| 132 |
for provider in self._provider_sequence(route):
|
|
@@ -141,7 +276,19 @@ class ProviderNeutralModelGateway:
|
|
| 141 |
f"model_provider_unavailable:{','.join(missing) or route.provider}:{capability}"
|
| 142 |
)
|
| 143 |
|
| 144 |
-
def stream(self, *, capability: str, **kwargs):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
route = self._route(capability)
|
| 146 |
missing = []
|
| 147 |
for provider in self._provider_sequence(route):
|
|
@@ -158,20 +305,17 @@ class ProviderNeutralModelGateway:
|
|
| 158 |
)
|
| 159 |
|
| 160 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
def resolve_plan_policy_snapshot(
|
| 162 |
app_config: dict[str, Any], request: dict[str, Any] | None = None
|
| 163 |
) -> dict[str, Any]:
|
| 164 |
-
"""Freeze a config-driven plan policy without touching payment authority.
|
| 165 |
-
|
| 166 |
-
A2 only creates the interface. Worker/D1 remain the authority for payment,
|
| 167 |
-
quota and entitlement; if the request does not yet carry a trusted plan_id,
|
| 168 |
-
the legacy-current policy preserves existing production behavior.
|
| 169 |
-
"""
|
| 170 |
req = dict(request or {})
|
| 171 |
policies = dict(app_config.get("plan_policies") or {})
|
| 172 |
-
# Never trust a browser/request plan label as an entitlement authority.
|
| 173 |
-
# A future Worker/D1 integration may inject a trusted entitlement snapshot;
|
| 174 |
-
# until then every task stays on the legacy-current production policy.
|
| 175 |
entitlement = req.get("entitlement_snapshot") if isinstance(req.get("entitlement_snapshot"), dict) else {}
|
| 176 |
requested = str(
|
| 177 |
req.get("trusted_plan_id") or entitlement.get("plan_id") or ""
|
|
|
|
| 1 |
# -*- coding: utf-8 -*-
|
| 2 |
"""Provider-neutral model routing primitives for SmartPaper.
|
| 3 |
|
| 4 |
+
Legacy tasks continue to route by coarse capability. A5-R3-R4B adds a
|
| 5 |
+
separate, trusted product-policy seam: academic callers provide an explicit
|
| 6 |
+
logical_node, while the frozen TaskPolicySnapshot supplies provider/model and
|
| 7 |
+
execution settings. Product routing never falls back to legacy capability
|
| 8 |
+
routes.
|
| 9 |
"""
|
| 10 |
from __future__ import annotations
|
| 11 |
|
|
|
|
| 41 |
fallback_providers: tuple[str, ...] = ()
|
| 42 |
|
| 43 |
|
| 44 |
+
@dataclass(frozen=True)
|
| 45 |
+
class TrustedProductRoute:
|
| 46 |
+
provider: str
|
| 47 |
+
model_id: str
|
| 48 |
+
thinking_enabled: bool
|
| 49 |
+
reasoning_effort: str | None
|
| 50 |
+
max_output_tokens: int
|
| 51 |
+
retry_budget: int
|
| 52 |
|
| 53 |
+
|
| 54 |
+
class ProviderNeutralModelGateway:
|
| 55 |
+
"""Route legacy capabilities or trusted product logical nodes to adapters."""
|
|
|
|
|
|
|
| 56 |
|
| 57 |
def __init__(self, config: dict[str, Any] | None = None):
|
| 58 |
cfg = dict(config or {})
|
|
|
|
| 66 |
if isinstance(row, str):
|
| 67 |
provider = str(row or self.default_provider).strip().lower()
|
| 68 |
provider_role = cap
|
| 69 |
+
fallbacks = ()
|
| 70 |
else:
|
| 71 |
item = dict(row or {})
|
| 72 |
provider = str(item.get("provider") or self.default_provider).strip().lower()
|
|
|
|
| 79 |
for name in raw_fallbacks
|
| 80 |
if str(name or "").strip()
|
| 81 |
)
|
| 82 |
+
self._routes[cap] = ModelRoute(
|
| 83 |
+
provider=provider,
|
| 84 |
+
provider_role=provider_role,
|
| 85 |
+
fallback_providers=fallbacks,
|
| 86 |
+
)
|
| 87 |
self._call_adapters: dict[str, Callable[..., str]] = {}
|
| 88 |
self._stream_adapters: dict[str, Callable[..., Iterable[str]]] = {}
|
| 89 |
self._policy_snapshot: dict[str, Any] = {}
|
| 90 |
+
self._trusted_product_policy_snapshot: dict[str, Any] = {}
|
| 91 |
|
| 92 |
def register_provider(
|
| 93 |
self,
|
|
|
|
| 102 |
self._call_adapters[name] = call_adapter
|
| 103 |
self._stream_adapters[name] = stream_adapter
|
| 104 |
|
| 105 |
+
# Legacy config/request policy. It remains a separate trust domain.
|
| 106 |
def set_policy_snapshot(self, snapshot: dict[str, Any] | None) -> None:
|
| 107 |
self._policy_snapshot = deepcopy(dict(snapshot or {}))
|
| 108 |
|
|
|
|
| 110 |
def policy_snapshot(self) -> dict[str, Any]:
|
| 111 |
return deepcopy(self._policy_snapshot)
|
| 112 |
|
| 113 |
+
def set_trusted_product_policy_snapshot(self, snapshot: dict[str, Any] | None) -> None:
|
| 114 |
+
"""Attach a Worker/D1-verified TaskPolicySnapshot for product execution.
|
| 115 |
+
|
| 116 |
+
The runner validates task identity and snapshot SHA before calling this
|
| 117 |
+
setter. The gateway still validates route shape and fails closed before
|
| 118 |
+
invoking any provider adapter.
|
| 119 |
+
"""
|
| 120 |
+
self._trusted_product_policy_snapshot = deepcopy(dict(snapshot or {}))
|
| 121 |
+
|
| 122 |
+
@property
|
| 123 |
+
def trusted_product_policy_snapshot(self) -> dict[str, Any]:
|
| 124 |
+
return deepcopy(self._trusted_product_policy_snapshot)
|
| 125 |
+
|
| 126 |
def _route(self, capability: str) -> ModelRoute:
|
| 127 |
cap = str(capability or "reasoning").strip().lower() or "reasoning"
|
| 128 |
policy_routes = dict(self._policy_snapshot.get("model_routes") or {})
|
|
|
|
| 137 |
provider=str(row.get("provider") or self.default_provider).strip().lower(),
|
| 138 |
provider_role=str(row.get("provider_role") or cap).strip().lower(),
|
| 139 |
fallback_providers=tuple(
|
| 140 |
+
str(name or "").strip().lower()
|
| 141 |
+
for name in raw_fallbacks
|
| 142 |
if str(name or "").strip()
|
| 143 |
),
|
| 144 |
)
|
|
|
|
| 150 |
name for name in (route.provider, *route.fallback_providers) if name
|
| 151 |
))
|
| 152 |
|
| 153 |
+
def _trusted_product_context(self) -> tuple[dict[str, Any], dict[str, Any]] | None:
|
| 154 |
+
snapshot = self._trusted_product_policy_snapshot
|
| 155 |
+
if not snapshot:
|
| 156 |
+
return None
|
| 157 |
+
resolved = snapshot.get("resolved_policy")
|
| 158 |
+
if not isinstance(resolved, dict):
|
| 159 |
+
raise ModelRequestError("trusted_product_policy_invalid")
|
| 160 |
+
model = resolved.get("model")
|
| 161 |
+
if not isinstance(model, dict):
|
| 162 |
+
raise ModelRequestError("trusted_product_model_policy_missing")
|
| 163 |
+
routes = model.get("routes")
|
| 164 |
+
if not isinstance(routes, dict) or not routes:
|
| 165 |
+
raise ModelRequestError("trusted_product_model_routes_missing")
|
| 166 |
+
return resolved, routes
|
| 167 |
+
|
| 168 |
+
def _trusted_product_route(self, logical_node: str | None) -> tuple[TrustedProductRoute, str]:
|
| 169 |
+
context = self._trusted_product_context()
|
| 170 |
+
if context is None:
|
| 171 |
+
raise ModelRequestError("trusted_product_policy_missing")
|
| 172 |
+
resolved, routes = context
|
| 173 |
+
node = str(logical_node or "").strip()
|
| 174 |
+
if not node:
|
| 175 |
+
raise ModelRequestError("trusted_product_logical_node_required")
|
| 176 |
+
row = routes.get(node)
|
| 177 |
+
if not isinstance(row, dict):
|
| 178 |
+
raise ModelRequestError(f"trusted_product_route_missing:{node}")
|
| 179 |
+
|
| 180 |
+
provider = str(row.get("provider") or "").strip().lower()
|
| 181 |
+
model_id = str(row.get("model_id_or_alias") or "").strip()
|
| 182 |
+
if not provider or not model_id:
|
| 183 |
+
raise ModelRequestError(f"trusted_product_route_invalid:{node}")
|
| 184 |
+
|
| 185 |
+
thinking = row.get("thinking_enabled")
|
| 186 |
+
if not isinstance(thinking, bool):
|
| 187 |
+
raise ModelRequestError(f"trusted_product_route_thinking_invalid:{node}")
|
| 188 |
+
effort = row.get("reasoning_effort")
|
| 189 |
+
if thinking:
|
| 190 |
+
if str(effort or "").strip().lower() != "high":
|
| 191 |
+
raise ModelRequestError(f"trusted_product_route_effort_invalid:{node}")
|
| 192 |
+
effort = "high"
|
| 193 |
+
else:
|
| 194 |
+
if effort not in (None, ""):
|
| 195 |
+
raise ModelRequestError(f"trusted_product_route_effort_invalid:{node}")
|
| 196 |
+
effort = None
|
| 197 |
+
|
| 198 |
+
try:
|
| 199 |
+
max_output_tokens = int(row.get("max_output_tokens"))
|
| 200 |
+
retry_budget = int(row.get("retry_budget"))
|
| 201 |
+
except (TypeError, ValueError):
|
| 202 |
+
raise ModelRequestError(f"trusted_product_route_budget_invalid:{node}")
|
| 203 |
+
if max_output_tokens <= 0 or retry_budget < 0:
|
| 204 |
+
raise ModelRequestError(f"trusted_product_route_budget_invalid:{node}")
|
| 205 |
+
if row.get("escalation_model_id_or_alias") not in (None, ""):
|
| 206 |
+
raise ModelRequestError(f"trusted_product_route_escalation_forbidden:{node}")
|
| 207 |
+
if row.get("escalation_condition_code") not in (None, ""):
|
| 208 |
+
raise ModelRequestError(f"trusted_product_route_escalation_forbidden:{node}")
|
| 209 |
+
|
| 210 |
+
model_policy_version = str(
|
| 211 |
+
resolved.get("model_policy_version")
|
| 212 |
+
or snapshot_model_policy_version(self._trusted_product_policy_snapshot)
|
| 213 |
+
or ""
|
| 214 |
+
).strip()
|
| 215 |
+
return TrustedProductRoute(
|
| 216 |
+
provider=provider,
|
| 217 |
+
model_id=model_id,
|
| 218 |
+
thinking_enabled=thinking,
|
| 219 |
+
reasoning_effort=effort,
|
| 220 |
+
max_output_tokens=max_output_tokens,
|
| 221 |
+
retry_budget=retry_budget,
|
| 222 |
+
), model_policy_version
|
| 223 |
+
|
| 224 |
+
@staticmethod
|
| 225 |
+
def _effective_max_tokens(requested: Any, ceiling: int) -> int:
|
| 226 |
+
if requested is None:
|
| 227 |
+
return int(ceiling)
|
| 228 |
+
try:
|
| 229 |
+
value = int(requested)
|
| 230 |
+
except (TypeError, ValueError):
|
| 231 |
+
raise ModelRequestError("trusted_product_max_tokens_invalid")
|
| 232 |
+
if value <= 0:
|
| 233 |
+
raise ModelRequestError("trusted_product_max_tokens_invalid")
|
| 234 |
+
return min(value, int(ceiling))
|
| 235 |
+
|
| 236 |
+
def _product_payload(self, *, capability: str, logical_node: str | None, kwargs: dict[str, Any]):
|
| 237 |
+
route, model_policy_version = self._trusted_product_route(logical_node)
|
| 238 |
+
payload = dict(kwargs)
|
| 239 |
+
payload["model_role"] = str(capability or "reasoning").strip().lower() or "reasoning"
|
| 240 |
+
payload["model_name"] = route.model_id
|
| 241 |
+
payload["thinking"] = route.thinking_enabled
|
| 242 |
+
payload["reasoning_effort"] = route.reasoning_effort
|
| 243 |
+
payload["allow_fallback"] = False
|
| 244 |
+
payload["max_tokens"] = self._effective_max_tokens(
|
| 245 |
+
payload.get("max_tokens"), route.max_output_tokens
|
| 246 |
+
)
|
| 247 |
+
payload["retry_budget"] = route.retry_budget
|
| 248 |
+
payload["logical_node"] = str(logical_node or "").strip()
|
| 249 |
+
payload["model_policy_version"] = model_policy_version
|
| 250 |
+
payload["route_enforced"] = True
|
| 251 |
+
return route.provider, payload
|
| 252 |
+
|
| 253 |
+
def call(self, *, capability: str, logical_node: str | None = None, **kwargs) -> str:
|
| 254 |
+
if self._trusted_product_context() is not None:
|
| 255 |
+
provider, payload = self._product_payload(
|
| 256 |
+
capability=capability, logical_node=logical_node, kwargs=kwargs
|
| 257 |
+
)
|
| 258 |
+
adapter = self._call_adapters.get(provider)
|
| 259 |
+
if not callable(adapter):
|
| 260 |
+
raise ModelProviderUnavailableError(
|
| 261 |
+
f"model_provider_unavailable:{provider}:{logical_node or capability}"
|
| 262 |
+
)
|
| 263 |
+
return adapter(**payload)
|
| 264 |
+
|
| 265 |
route = self._route(capability)
|
| 266 |
missing = []
|
| 267 |
for provider in self._provider_sequence(route):
|
|
|
|
| 276 |
f"model_provider_unavailable:{','.join(missing) or route.provider}:{capability}"
|
| 277 |
)
|
| 278 |
|
| 279 |
+
def stream(self, *, capability: str, logical_node: str | None = None, **kwargs):
|
| 280 |
+
if self._trusted_product_context() is not None:
|
| 281 |
+
provider, payload = self._product_payload(
|
| 282 |
+
capability=capability, logical_node=logical_node, kwargs=kwargs
|
| 283 |
+
)
|
| 284 |
+
adapter = self._stream_adapters.get(provider)
|
| 285 |
+
if not callable(adapter):
|
| 286 |
+
raise ModelProviderUnavailableError(
|
| 287 |
+
f"model_provider_unavailable:{provider}:{logical_node or capability}"
|
| 288 |
+
)
|
| 289 |
+
yield from adapter(**payload)
|
| 290 |
+
return
|
| 291 |
+
|
| 292 |
route = self._route(capability)
|
| 293 |
missing = []
|
| 294 |
for provider in self._provider_sequence(route):
|
|
|
|
| 305 |
)
|
| 306 |
|
| 307 |
|
| 308 |
+
def snapshot_model_policy_version(snapshot: dict[str, Any] | None) -> str:
|
| 309 |
+
data = dict(snapshot or {})
|
| 310 |
+
return str(data.get("model_policy_version") or "").strip()
|
| 311 |
+
|
| 312 |
+
|
| 313 |
def resolve_plan_policy_snapshot(
|
| 314 |
app_config: dict[str, Any], request: dict[str, Any] | None = None
|
| 315 |
) -> dict[str, Any]:
|
| 316 |
+
"""Freeze a config-driven legacy plan policy without touching payment authority."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
req = dict(request or {})
|
| 318 |
policies = dict(app_config.get("plan_policies") or {})
|
|
|
|
|
|
|
|
|
|
| 319 |
entitlement = req.get("entitlement_snapshot") if isinstance(req.get("entitlement_snapshot"), dict) else {}
|
| 320 |
requested = str(
|
| 321 |
req.get("trusted_plan_id") or entitlement.get("plan_id") or ""
|
core/pipeline_agent.py
CHANGED
|
@@ -272,7 +272,7 @@ class SSciPipelineAgent:
|
|
| 272 |
self, *, usage, model, role, attempt_index, streaming, thinking,
|
| 273 |
reasoning_effort, system_prompt, user_prompt, started_at,
|
| 274 |
completed_at, first_token_at=None, finish_reason="",
|
| 275 |
-
response_payload=None,
|
| 276 |
):
|
| 277 |
if not self._model_usage_enabled():
|
| 278 |
return
|
|
@@ -308,6 +308,9 @@ class SSciPipelineAgent:
|
|
| 308 |
metadata=sanitize_metadata({
|
| 309 |
"system_fingerprint": response_payload.get("system_fingerprint"),
|
| 310 |
"reasoning_effort": reasoning_effort,
|
|
|
|
|
|
|
|
|
|
| 311 |
}),
|
| 312 |
)
|
| 313 |
emit_usage_event(getattr(self, "_model_usage_sink", None), event)
|
|
@@ -851,8 +854,13 @@ class SSciPipelineAgent:
|
|
| 851 |
requested_model,
|
| 852 |
requested_thinking,
|
| 853 |
allow_fallback,
|
|
|
|
| 854 |
):
|
| 855 |
-
total =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 856 |
fallback_model = self.deepseek_models.get("fallback", "")
|
| 857 |
for attempt in range(total):
|
| 858 |
model = requested_model
|
|
@@ -877,7 +885,8 @@ class SSciPipelineAgent:
|
|
| 877 |
thinking=None,
|
| 878 |
reasoning_effort=None,
|
| 879 |
allow_fallback=None,
|
| 880 |
-
response_format=None,
|
|
|
|
| 881 |
):
|
| 882 |
if not str(getattr(self, "ds_key", "") or "").strip():
|
| 883 |
raise DeepSeekAuthenticationError(
|
|
@@ -896,6 +905,15 @@ class SSciPipelineAgent:
|
|
| 896 |
reasoning_effort=reasoning_effort,
|
| 897 |
allow_fallback=allow_fallback,
|
| 898 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 899 |
tokens_est = self._count_tokens(str(user_prompt))
|
| 900 |
|
| 901 |
logging.info(
|
|
@@ -913,6 +931,7 @@ class SSciPipelineAgent:
|
|
| 913 |
requested_model,
|
| 914 |
requested_thinking,
|
| 915 |
resolved_fallback,
|
|
|
|
| 916 |
):
|
| 917 |
with self.lock:
|
| 918 |
self.ds_calls += 1
|
|
@@ -942,7 +961,7 @@ class SSciPipelineAgent:
|
|
| 942 |
logging.info(
|
| 943 |
"DeepSeek static attempt start | attempt=%s/%s | model=%s",
|
| 944 |
attempt + 1,
|
| 945 |
-
|
| 946 |
current_model,
|
| 947 |
)
|
| 948 |
response = requests.post(
|
|
@@ -1000,6 +1019,8 @@ class SSciPipelineAgent:
|
|
| 1000 |
completed_at=completed_at,
|
| 1001 |
finish_reason=choices[0].get("finish_reason"),
|
| 1002 |
response_payload=result,
|
|
|
|
|
|
|
| 1003 |
)
|
| 1004 |
return content
|
| 1005 |
|
|
@@ -1016,11 +1037,11 @@ class SSciPipelineAgent:
|
|
| 1016 |
DeepSeekAPIError,
|
| 1017 |
) as exc:
|
| 1018 |
last_error = exc
|
| 1019 |
-
is_last = attempt ==
|
| 1020 |
logging.error(
|
| 1021 |
"DeepSeek attempt %s/%s failed: %s",
|
| 1022 |
attempt + 1,
|
| 1023 |
-
|
| 1024 |
exc,
|
| 1025 |
)
|
| 1026 |
self._touch_execution_progress("deepseek_static_retry")
|
|
@@ -1030,7 +1051,7 @@ class SSciPipelineAgent:
|
|
| 1030 |
|
| 1031 |
raise DeepSeekAPIError(
|
| 1032 |
"DeepSeek API failed after "
|
| 1033 |
-
f"{
|
| 1034 |
)
|
| 1035 |
|
| 1036 |
def _stream_deepseek_transport(
|
|
@@ -1044,7 +1065,8 @@ class SSciPipelineAgent:
|
|
| 1044 |
reasoning_effort=None,
|
| 1045 |
temp=0.3,
|
| 1046 |
allow_fallback=None,
|
| 1047 |
-
reasoning_only_recovery=False,
|
|
|
|
| 1048 |
):
|
| 1049 |
if not str(getattr(self, "ds_key", "") or "").strip():
|
| 1050 |
raise DeepSeekAuthenticationError(
|
|
@@ -1063,6 +1085,15 @@ class SSciPipelineAgent:
|
|
| 1063 |
reasoning_effort=reasoning_effort,
|
| 1064 |
allow_fallback=allow_fallback,
|
| 1065 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1066 |
tokens_est = self._count_tokens(str(user_prompt))
|
| 1067 |
|
| 1068 |
logging.info(
|
|
@@ -1080,6 +1111,7 @@ class SSciPipelineAgent:
|
|
| 1080 |
requested_model,
|
| 1081 |
requested_thinking,
|
| 1082 |
resolved_fallback,
|
|
|
|
| 1083 |
):
|
| 1084 |
with self.lock:
|
| 1085 |
self.ds_calls += 1
|
|
@@ -1121,7 +1153,7 @@ class SSciPipelineAgent:
|
|
| 1121 |
logging.info(
|
| 1122 |
"DeepSeek stream attempt start | attempt=%s/%s | model=%s | idle_limit=%ss",
|
| 1123 |
attempt + 1,
|
| 1124 |
-
|
| 1125 |
current_model,
|
| 1126 |
self.deepseek_stream_idle_seconds,
|
| 1127 |
)
|
|
@@ -1260,6 +1292,8 @@ class SSciPipelineAgent:
|
|
| 1260 |
first_token_at=first_content_at,
|
| 1261 |
finish_reason=stream_finish_reason,
|
| 1262 |
response_payload=stream_response_payload,
|
|
|
|
|
|
|
| 1263 |
)
|
| 1264 |
return
|
| 1265 |
|
|
@@ -1285,7 +1319,7 @@ class SSciPipelineAgent:
|
|
| 1285 |
logging.error(
|
| 1286 |
"DeepSeek stream attempt %s/%s failed: %s",
|
| 1287 |
attempt + 1,
|
| 1288 |
-
|
| 1289 |
exc,
|
| 1290 |
)
|
| 1291 |
|
|
@@ -1296,24 +1330,28 @@ class SSciPipelineAgent:
|
|
| 1296 |
) from exc
|
| 1297 |
|
| 1298 |
self._touch_execution_progress("deepseek_stream_retry")
|
| 1299 |
-
is_last = attempt ==
|
| 1300 |
if is_last:
|
| 1301 |
break
|
| 1302 |
time.sleep(self._retry_delay(attempt))
|
| 1303 |
|
| 1304 |
raise DeepSeekAPIError(
|
| 1305 |
"DeepSeek streaming failed after "
|
| 1306 |
-
f"{
|
| 1307 |
)
|
| 1308 |
|
| 1309 |
def set_model_policy_snapshot(self, snapshot):
|
| 1310 |
self.model_policy_snapshot = dict(snapshot or {})
|
| 1311 |
self.model_gateway.set_policy_snapshot(self.model_policy_snapshot)
|
| 1312 |
|
|
|
|
|
|
|
|
|
|
| 1313 |
def call_model(
|
| 1314 |
self, system_prompt, user_prompt, max_tokens=8000, temp=0.3,
|
| 1315 |
model_name=None, model_role="reasoning", thinking=None,
|
| 1316 |
reasoning_effort=None, allow_fallback=None, response_format=None,
|
|
|
|
| 1317 |
):
|
| 1318 |
gateway = getattr(self, "model_gateway", None)
|
| 1319 |
if gateway is None:
|
|
@@ -1326,7 +1364,8 @@ class SSciPipelineAgent:
|
|
| 1326 |
response_format=response_format,
|
| 1327 |
)
|
| 1328 |
return gateway.call(
|
| 1329 |
-
capability=model_role,
|
|
|
|
| 1330 |
max_tokens=max_tokens, temp=temp, model_name=model_name,
|
| 1331 |
thinking=thinking, reasoning_effort=reasoning_effort,
|
| 1332 |
allow_fallback=allow_fallback, response_format=response_format,
|
|
@@ -1335,7 +1374,7 @@ class SSciPipelineAgent:
|
|
| 1335 |
def stream_model(
|
| 1336 |
self, system_prompt, user_prompt, max_tokens=8000, model_name=None,
|
| 1337 |
model_role="reasoning", thinking=None, reasoning_effort=None, temp=0.3,
|
| 1338 |
-
allow_fallback=None, reasoning_only_recovery=False,
|
| 1339 |
):
|
| 1340 |
gateway = getattr(self, "model_gateway", None)
|
| 1341 |
if gateway is None:
|
|
@@ -1347,7 +1386,8 @@ class SSciPipelineAgent:
|
|
| 1347 |
)
|
| 1348 |
return
|
| 1349 |
yield from gateway.stream(
|
| 1350 |
-
capability=model_role,
|
|
|
|
| 1351 |
max_tokens=max_tokens, model_name=model_name, thinking=thinking,
|
| 1352 |
reasoning_effort=reasoning_effort, temp=temp, allow_fallback=allow_fallback,
|
| 1353 |
reasoning_only_recovery=reasoning_only_recovery,
|
|
@@ -1359,24 +1399,25 @@ class SSciPipelineAgent:
|
|
| 1359 |
self, system_prompt, user_prompt, max_tokens=8000, temp=0.3,
|
| 1360 |
model_name=None, model_role="reasoning", thinking=None,
|
| 1361 |
reasoning_effort=None, allow_fallback=None, response_format=None,
|
|
|
|
| 1362 |
):
|
| 1363 |
return self.call_model(
|
| 1364 |
system_prompt, user_prompt, max_tokens=max_tokens, temp=temp,
|
| 1365 |
model_name=model_name, model_role=model_role, thinking=thinking,
|
| 1366 |
reasoning_effort=reasoning_effort, allow_fallback=allow_fallback,
|
| 1367 |
-
response_format=response_format,
|
| 1368 |
)
|
| 1369 |
|
| 1370 |
def stream_deepseek(
|
| 1371 |
self, system_prompt, user_prompt, max_tokens=8000, model_name=None,
|
| 1372 |
model_role="reasoning", thinking=None, reasoning_effort=None, temp=0.3,
|
| 1373 |
-
allow_fallback=None, reasoning_only_recovery=False,
|
| 1374 |
):
|
| 1375 |
yield from self.stream_model(
|
| 1376 |
system_prompt, user_prompt, max_tokens=max_tokens, model_name=model_name,
|
| 1377 |
model_role=model_role, thinking=thinking, reasoning_effort=reasoning_effort,
|
| 1378 |
temp=temp, allow_fallback=allow_fallback,
|
| 1379 |
-
reasoning_only_recovery=reasoning_only_recovery,
|
| 1380 |
)
|
| 1381 |
|
| 1382 |
def parse_user_core_literature(self, user_lit_text):
|
|
@@ -1398,6 +1439,7 @@ class SSciPipelineAgent:
|
|
| 1398 |
model_role="reasoning",
|
| 1399 |
thinking=True,
|
| 1400 |
allow_fallback=False,
|
|
|
|
| 1401 |
)
|
| 1402 |
papers = self._extract_json_from_text(response)
|
| 1403 |
if not papers:
|
|
@@ -1461,7 +1503,7 @@ class SSciPipelineAgent:
|
|
| 1461 |
user_prompt = APP_CONFIG["prompt_templates"]["synthesis_user"].replace(
|
| 1462 |
"{topic}", topic).replace("{user_injection}", user_injection).replace(
|
| 1463 |
"{refs_text}", refs_text).replace("{directive}", APP_CONFIG['deep_synthesis_directive'])
|
| 1464 |
-
return self.call_deepseek(sys_prompt, user_prompt, temp=0.2)
|
| 1465 |
|
| 1466 |
def generate_anchors(
|
| 1467 |
self,
|
|
@@ -1504,6 +1546,7 @@ class SSciPipelineAgent:
|
|
| 1504 |
model_role="reasoning",
|
| 1505 |
thinking=False,
|
| 1506 |
allow_fallback=False,
|
|
|
|
| 1507 |
)
|
| 1508 |
|
| 1509 |
return _stream_direct_synthesis()
|
|
@@ -1562,6 +1605,7 @@ Return a concise PROVISIONAL RESEARCH LOGIC CONTRACT in plain English prose. It
|
|
| 1562 |
model_role="reasoning",
|
| 1563 |
thinking=False,
|
| 1564 |
allow_fallback=False,
|
|
|
|
| 1565 |
)
|
| 1566 |
|
| 1567 |
return _stream_direct_research_logic()
|
|
@@ -1668,6 +1712,7 @@ Return a concise PROVISIONAL RESEARCH LOGIC CONTRACT in plain English prose. It
|
|
| 1668 |
thinking=False,
|
| 1669 |
allow_fallback=False,
|
| 1670 |
response_format={"type": "json_object"},
|
|
|
|
| 1671 |
)
|
| 1672 |
try:
|
| 1673 |
parsed = self._extract_json_from_text(response)
|
|
@@ -2630,6 +2675,7 @@ Return ONLY a valid JSON array of strings."""
|
|
| 2630 |
user_prompt,
|
| 2631 |
temp=0.1 + attempt * 0.1,
|
| 2632 |
model_role="fast",
|
|
|
|
| 2633 |
))
|
| 2634 |
if queries and isinstance(queries, list) and len(queries) > 0:
|
| 2635 |
final_queries = [str(q).strip() for q in queries if self._is_english_dominant(str(q).strip())]
|
|
@@ -3082,6 +3128,7 @@ Return ONLY a valid JSON array of strings."""
|
|
| 3082 |
f"Topic: {topic}\nKeywords: {keywords}\nUsed: {sorted(all_used_terms)}",
|
| 3083 |
temp=0.4,
|
| 3084 |
model_role='fast',
|
|
|
|
| 3085 |
)
|
| 3086 |
parsed = self._extract_json_from_text(response)
|
| 3087 |
terms = parsed if isinstance(parsed, list) else []
|
|
@@ -3287,6 +3334,7 @@ INPUT:
|
|
| 3287 |
model_role='reasoning',
|
| 3288 |
thinking=False,
|
| 3289 |
allow_fallback=False,
|
|
|
|
| 3290 |
)
|
| 3291 |
parsed = self._extract_json_from_text(raw)
|
| 3292 |
parsed_rows = parsed.get('assignments') if isinstance(parsed, dict) else None
|
|
@@ -5243,6 +5291,7 @@ INPUT:
|
|
| 5243 |
model_role='reasoning',
|
| 5244 |
thinking=False,
|
| 5245 |
allow_fallback=False,
|
|
|
|
| 5246 |
)
|
| 5247 |
parsed = self._extract_json_from_text(response)
|
| 5248 |
if isinstance(parsed, dict):
|
|
@@ -5386,6 +5435,7 @@ Return exactly:
|
|
| 5386 |
model_role="reasoning",
|
| 5387 |
thinking=False,
|
| 5388 |
allow_fallback=False,
|
|
|
|
| 5389 |
)
|
| 5390 |
parsed = self._extract_json_from_text(response)
|
| 5391 |
last_error = None
|
|
@@ -5439,6 +5489,7 @@ Return exactly:
|
|
| 5439 |
model_role="reasoning",
|
| 5440 |
thinking=False,
|
| 5441 |
allow_fallback=False,
|
|
|
|
| 5442 |
)
|
| 5443 |
parsed = self._extract_json_from_text(response)
|
| 5444 |
by_id = index_items(parsed)
|
|
@@ -5911,6 +5962,7 @@ TEXT:
|
|
| 5911 |
model_role='writing',
|
| 5912 |
thinking=False,
|
| 5913 |
allow_fallback=False,
|
|
|
|
| 5914 |
)
|
| 5915 |
revised = self._remove_ai_meta_traces(revised)
|
| 5916 |
original_numbers = self._numeric_tokens(value)
|
|
@@ -5963,6 +6015,7 @@ TEXT:
|
|
| 5963 |
raw = self.call_deepseek(
|
| 5964 |
'You are an independent senior management-journal reviewer. Be exact and conservative.',
|
| 5965 |
prompt, max_tokens=800, temp=0.0, model_role='fast', thinking=False, allow_fallback=True,
|
|
|
|
| 5966 |
)
|
| 5967 |
except PipelineCancelledError:
|
| 5968 |
raise
|
|
@@ -6037,6 +6090,7 @@ CURRENT UNIT:
|
|
| 6037 |
repaired = self.call_model(
|
| 6038 |
'You are SmartPaper Adaptive Academic Repair. Preserve evidence truth and repair prose with Conciseness, Accuracy, and Rigor.',
|
| 6039 |
prompt, max_tokens=7000, temp=0.15, model_role='repair', thinking=True, allow_fallback=True,
|
|
|
|
| 6040 |
)
|
| 6041 |
except PipelineCancelledError:
|
| 6042 |
raise
|
|
@@ -8059,6 +8113,7 @@ CURRENT UNIT:
|
|
| 8059 |
json.dumps({'ir_version': self.CANONICAL_GLOSSARY_IR_VERSION, 'terms': payload}, ensure_ascii=False),
|
| 8060 |
max_tokens=max(1200, min(5000, 120 + len(payload) * 90)),
|
| 8061 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
|
|
|
| 8062 |
)
|
| 8063 |
parsed = self._extract_json_from_text(response)
|
| 8064 |
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
|
@@ -8321,6 +8376,7 @@ CURRENT UNIT:
|
|
| 8321 |
json.dumps({'ir_version': self.CANONICAL_GLOSSARY_IR_VERSION, 'terms': payload}, ensure_ascii=False),
|
| 8322 |
max_tokens=max(1200, min(4200, 180 + len(payload) * 120)),
|
| 8323 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
|
|
|
| 8324 |
)
|
| 8325 |
parsed = self._extract_json_from_text(response)
|
| 8326 |
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
|
@@ -8453,6 +8509,7 @@ CURRENT UNIT:
|
|
| 8453 |
json.dumps({'terms': missing}, ensure_ascii=False),
|
| 8454 |
max_tokens=max(1200, min(5000, len(missing) * 80)),
|
| 8455 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
|
|
|
| 8456 |
)
|
| 8457 |
parsed = self._extract_json_from_text(response)
|
| 8458 |
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
|
@@ -8600,6 +8657,7 @@ SECTION: {section_name}
|
|
| 8600 |
response = self.call_deepseek(
|
| 8601 |
'You are a bilingual academic translation equivalence auditor. Be conservative and evidence-preserving.',
|
| 8602 |
prompt, max_tokens=1200, temp=0.0, model_role='reasoning', thinking=False, allow_fallback=False,
|
|
|
|
| 8603 |
)
|
| 8604 |
parsed = self._extract_json_from_text(response)
|
| 8605 |
ok = parsed.get('ok') if isinstance(parsed, dict) else None
|
|
@@ -8691,6 +8749,7 @@ If ok=false, codes MUST contain only values from allowed_issue_codes. For NEGATI
|
|
| 8691 |
system_prompt, json.dumps(payload, ensure_ascii=False),
|
| 8692 |
max_tokens=max(1200, min(3600, 650 + 260 * len(rows))),
|
| 8693 |
temp=0.0, model_role='reasoning', thinking=False, allow_fallback=False,
|
|
|
|
| 8694 |
)
|
| 8695 |
parsed = self._extract_json_from_text(response)
|
| 8696 |
returned = parsed.get('items', []) if isinstance(parsed, dict) else []
|
|
@@ -8962,6 +9021,7 @@ Translate headings and terminology naturally. Preserve standard variable abbrevi
|
|
| 8962 |
model_role='translation',
|
| 8963 |
thinking=False,
|
| 8964 |
allow_fallback=False,
|
|
|
|
| 8965 |
)
|
| 8966 |
parsed = self._extract_json_from_text(response)
|
| 8967 |
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
|
@@ -9151,6 +9211,7 @@ Translate headings and terminology naturally. Preserve standard variable abbrevi
|
|
| 9151 |
effective_prompt, marked_source,
|
| 9152 |
max_tokens=max(1800, min(9000, self._count_tokens(marked_source) * 2 + 1200)),
|
| 9153 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
|
|
|
| 9154 |
)
|
| 9155 |
try:
|
| 9156 |
parsed = self._parse_marked_translation(result, expected_ids)
|
|
@@ -9336,6 +9397,7 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 9336 |
response = self.call_deepseek(
|
| 9337 |
prompt, json.dumps(payload, ensure_ascii=False), max_tokens=900, temp=0.0,
|
| 9338 |
model_role='reasoning', thinking=False, allow_fallback=False,
|
|
|
|
| 9339 |
)
|
| 9340 |
parsed = self._extract_json_from_text(response)
|
| 9341 |
raw = parsed.get('delta_codes', []) if isinstance(parsed, dict) else []
|
|
@@ -9432,6 +9494,7 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 9432 |
effective_prompt, marked_source,
|
| 9433 |
max_tokens=max(1200, min(2600, self._count_tokens(marked_source) * 2 + 900)),
|
| 9434 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
|
|
|
| 9435 |
)
|
| 9436 |
parsed = self._parse_marked_translation(result, [block_id])
|
| 9437 |
try:
|
|
@@ -9458,6 +9521,7 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 9458 |
strict_prompt, marked_source,
|
| 9459 |
max_tokens=max(1200, min(2600, self._count_tokens(marked_source) * 2 + 900)),
|
| 9460 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
|
|
|
| 9461 |
)
|
| 9462 |
strict_parsed = self._parse_marked_translation(strict_result, [block_id])
|
| 9463 |
restored = self._restore_translation_tokens(
|
|
@@ -9483,6 +9547,7 @@ Each code must be one of: {sorted(allowed)}. Return one or more specific dimensi
|
|
| 9483 |
numeric_prompt, marked_source,
|
| 9484 |
max_tokens=max(1200, min(2600, self._count_tokens(marked_source) * 2 + 900)),
|
| 9485 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
|
|
|
| 9486 |
)
|
| 9487 |
retry_parsed = self._parse_marked_translation(retry_result, [block_id])
|
| 9488 |
restored = self._restore_translation_tokens(
|
|
@@ -9856,6 +9921,7 @@ Decision must be CONFIRMED_BLOCKING or NOT_CONFIRMED. evidence_code must be one
|
|
| 9856 |
system_prompt, json.dumps(payload, ensure_ascii=False),
|
| 9857 |
max_tokens=max(1200, min(3200, 600 + 180 * len(model_rows))),
|
| 9858 |
temp=0.0, model_role='reasoning', thinking=False, allow_fallback=False,
|
|
|
|
| 9859 |
)
|
| 9860 |
parsed = self._extract_json_from_text(response)
|
| 9861 |
returned = parsed.get('items', []) if isinstance(parsed, dict) else []
|
|
@@ -10620,6 +10686,7 @@ SOURCE UNIT:
|
|
| 10620 |
translated = self.call_model(
|
| 10621 |
'You are SmartPaper Adaptive Translation. Produce faithful academic translation with no added or omitted claims.',
|
| 10622 |
prompt, max_tokens=8000, temp=0.1, model_role='translation', thinking=False, allow_fallback=True,
|
|
|
|
| 10623 |
)
|
| 10624 |
translated = re.sub(r'<think>.*?</think>', '', str(translated or ''), flags=re.S).strip()
|
| 10625 |
if not translated:
|
|
|
|
| 272 |
self, *, usage, model, role, attempt_index, streaming, thinking,
|
| 273 |
reasoning_effort, system_prompt, user_prompt, started_at,
|
| 274 |
completed_at, first_token_at=None, finish_reason="",
|
| 275 |
+
response_payload=None, logical_node="", model_policy_version="",
|
| 276 |
):
|
| 277 |
if not self._model_usage_enabled():
|
| 278 |
return
|
|
|
|
| 308 |
metadata=sanitize_metadata({
|
| 309 |
"system_fingerprint": response_payload.get("system_fingerprint"),
|
| 310 |
"reasoning_effort": reasoning_effort,
|
| 311 |
+
"logical_node": str(logical_node or ""),
|
| 312 |
+
"model_policy_version": str(model_policy_version or ""),
|
| 313 |
+
"resolved_model_id": str(response_payload.get("model") or model or ""),
|
| 314 |
}),
|
| 315 |
)
|
| 316 |
emit_usage_event(getattr(self, "_model_usage_sink", None), event)
|
|
|
|
| 854 |
requested_model,
|
| 855 |
requested_thinking,
|
| 856 |
allow_fallback,
|
| 857 |
+
retry_budget=None,
|
| 858 |
):
|
| 859 |
+
total = (
|
| 860 |
+
self.deepseek_max_retries
|
| 861 |
+
if retry_budget is None
|
| 862 |
+
else max(1, 1 + int(retry_budget))
|
| 863 |
+
)
|
| 864 |
fallback_model = self.deepseek_models.get("fallback", "")
|
| 865 |
for attempt in range(total):
|
| 866 |
model = requested_model
|
|
|
|
| 885 |
thinking=None,
|
| 886 |
reasoning_effort=None,
|
| 887 |
allow_fallback=None,
|
| 888 |
+
response_format=None, retry_budget=None, logical_node=None,
|
| 889 |
+
model_policy_version=None, route_enforced=False,
|
| 890 |
):
|
| 891 |
if not str(getattr(self, "ds_key", "") or "").strip():
|
| 892 |
raise DeepSeekAuthenticationError(
|
|
|
|
| 905 |
reasoning_effort=reasoning_effort,
|
| 906 |
allow_fallback=allow_fallback,
|
| 907 |
)
|
| 908 |
+
if route_enforced:
|
| 909 |
+
resolved_fallback = False
|
| 910 |
+
if not requested_thinking:
|
| 911 |
+
resolved_effort = None
|
| 912 |
+
attempt_total = (
|
| 913 |
+
self.deepseek_max_retries
|
| 914 |
+
if retry_budget is None
|
| 915 |
+
else max(1, 1 + int(retry_budget))
|
| 916 |
+
)
|
| 917 |
tokens_est = self._count_tokens(str(user_prompt))
|
| 918 |
|
| 919 |
logging.info(
|
|
|
|
| 931 |
requested_model,
|
| 932 |
requested_thinking,
|
| 933 |
resolved_fallback,
|
| 934 |
+
retry_budget=retry_budget,
|
| 935 |
):
|
| 936 |
with self.lock:
|
| 937 |
self.ds_calls += 1
|
|
|
|
| 961 |
logging.info(
|
| 962 |
"DeepSeek static attempt start | attempt=%s/%s | model=%s",
|
| 963 |
attempt + 1,
|
| 964 |
+
attempt_total,
|
| 965 |
current_model,
|
| 966 |
)
|
| 967 |
response = requests.post(
|
|
|
|
| 1019 |
completed_at=completed_at,
|
| 1020 |
finish_reason=choices[0].get("finish_reason"),
|
| 1021 |
response_payload=result,
|
| 1022 |
+
logical_node=logical_node,
|
| 1023 |
+
model_policy_version=model_policy_version,
|
| 1024 |
)
|
| 1025 |
return content
|
| 1026 |
|
|
|
|
| 1037 |
DeepSeekAPIError,
|
| 1038 |
) as exc:
|
| 1039 |
last_error = exc
|
| 1040 |
+
is_last = attempt == attempt_total - 1
|
| 1041 |
logging.error(
|
| 1042 |
"DeepSeek attempt %s/%s failed: %s",
|
| 1043 |
attempt + 1,
|
| 1044 |
+
attempt_total,
|
| 1045 |
exc,
|
| 1046 |
)
|
| 1047 |
self._touch_execution_progress("deepseek_static_retry")
|
|
|
|
| 1051 |
|
| 1052 |
raise DeepSeekAPIError(
|
| 1053 |
"DeepSeek API failed after "
|
| 1054 |
+
f"{attempt_total} attempts: {last_error}"
|
| 1055 |
)
|
| 1056 |
|
| 1057 |
def _stream_deepseek_transport(
|
|
|
|
| 1065 |
reasoning_effort=None,
|
| 1066 |
temp=0.3,
|
| 1067 |
allow_fallback=None,
|
| 1068 |
+
reasoning_only_recovery=False, retry_budget=None, logical_node=None,
|
| 1069 |
+
model_policy_version=None, route_enforced=False,
|
| 1070 |
):
|
| 1071 |
if not str(getattr(self, "ds_key", "") or "").strip():
|
| 1072 |
raise DeepSeekAuthenticationError(
|
|
|
|
| 1085 |
reasoning_effort=reasoning_effort,
|
| 1086 |
allow_fallback=allow_fallback,
|
| 1087 |
)
|
| 1088 |
+
if route_enforced:
|
| 1089 |
+
resolved_fallback = False
|
| 1090 |
+
if not requested_thinking:
|
| 1091 |
+
resolved_effort = None
|
| 1092 |
+
attempt_total = (
|
| 1093 |
+
self.deepseek_max_retries
|
| 1094 |
+
if retry_budget is None
|
| 1095 |
+
else max(1, 1 + int(retry_budget))
|
| 1096 |
+
)
|
| 1097 |
tokens_est = self._count_tokens(str(user_prompt))
|
| 1098 |
|
| 1099 |
logging.info(
|
|
|
|
| 1111 |
requested_model,
|
| 1112 |
requested_thinking,
|
| 1113 |
resolved_fallback,
|
| 1114 |
+
retry_budget=retry_budget,
|
| 1115 |
):
|
| 1116 |
with self.lock:
|
| 1117 |
self.ds_calls += 1
|
|
|
|
| 1153 |
logging.info(
|
| 1154 |
"DeepSeek stream attempt start | attempt=%s/%s | model=%s | idle_limit=%ss",
|
| 1155 |
attempt + 1,
|
| 1156 |
+
attempt_total,
|
| 1157 |
current_model,
|
| 1158 |
self.deepseek_stream_idle_seconds,
|
| 1159 |
)
|
|
|
|
| 1292 |
first_token_at=first_content_at,
|
| 1293 |
finish_reason=stream_finish_reason,
|
| 1294 |
response_payload=stream_response_payload,
|
| 1295 |
+
logical_node=logical_node,
|
| 1296 |
+
model_policy_version=model_policy_version,
|
| 1297 |
)
|
| 1298 |
return
|
| 1299 |
|
|
|
|
| 1319 |
logging.error(
|
| 1320 |
"DeepSeek stream attempt %s/%s failed: %s",
|
| 1321 |
attempt + 1,
|
| 1322 |
+
attempt_total,
|
| 1323 |
exc,
|
| 1324 |
)
|
| 1325 |
|
|
|
|
| 1330 |
) from exc
|
| 1331 |
|
| 1332 |
self._touch_execution_progress("deepseek_stream_retry")
|
| 1333 |
+
is_last = attempt == attempt_total - 1
|
| 1334 |
if is_last:
|
| 1335 |
break
|
| 1336 |
time.sleep(self._retry_delay(attempt))
|
| 1337 |
|
| 1338 |
raise DeepSeekAPIError(
|
| 1339 |
"DeepSeek streaming failed after "
|
| 1340 |
+
f"{attempt_total} attempts: {last_error}"
|
| 1341 |
)
|
| 1342 |
|
| 1343 |
def set_model_policy_snapshot(self, snapshot):
|
| 1344 |
self.model_policy_snapshot = dict(snapshot or {})
|
| 1345 |
self.model_gateway.set_policy_snapshot(self.model_policy_snapshot)
|
| 1346 |
|
| 1347 |
+
def set_trusted_product_policy_snapshot(self, snapshot):
|
| 1348 |
+
self.model_gateway.set_trusted_product_policy_snapshot(snapshot)
|
| 1349 |
+
|
| 1350 |
def call_model(
|
| 1351 |
self, system_prompt, user_prompt, max_tokens=8000, temp=0.3,
|
| 1352 |
model_name=None, model_role="reasoning", thinking=None,
|
| 1353 |
reasoning_effort=None, allow_fallback=None, response_format=None,
|
| 1354 |
+
logical_node=None,
|
| 1355 |
):
|
| 1356 |
gateway = getattr(self, "model_gateway", None)
|
| 1357 |
if gateway is None:
|
|
|
|
| 1364 |
response_format=response_format,
|
| 1365 |
)
|
| 1366 |
return gateway.call(
|
| 1367 |
+
capability=model_role, logical_node=logical_node,
|
| 1368 |
+
system_prompt=system_prompt, user_prompt=user_prompt,
|
| 1369 |
max_tokens=max_tokens, temp=temp, model_name=model_name,
|
| 1370 |
thinking=thinking, reasoning_effort=reasoning_effort,
|
| 1371 |
allow_fallback=allow_fallback, response_format=response_format,
|
|
|
|
| 1374 |
def stream_model(
|
| 1375 |
self, system_prompt, user_prompt, max_tokens=8000, model_name=None,
|
| 1376 |
model_role="reasoning", thinking=None, reasoning_effort=None, temp=0.3,
|
| 1377 |
+
allow_fallback=None, reasoning_only_recovery=False, logical_node=None,
|
| 1378 |
):
|
| 1379 |
gateway = getattr(self, "model_gateway", None)
|
| 1380 |
if gateway is None:
|
|
|
|
| 1386 |
)
|
| 1387 |
return
|
| 1388 |
yield from gateway.stream(
|
| 1389 |
+
capability=model_role, logical_node=logical_node,
|
| 1390 |
+
system_prompt=system_prompt, user_prompt=user_prompt,
|
| 1391 |
max_tokens=max_tokens, model_name=model_name, thinking=thinking,
|
| 1392 |
reasoning_effort=reasoning_effort, temp=temp, allow_fallback=allow_fallback,
|
| 1393 |
reasoning_only_recovery=reasoning_only_recovery,
|
|
|
|
| 1399 |
self, system_prompt, user_prompt, max_tokens=8000, temp=0.3,
|
| 1400 |
model_name=None, model_role="reasoning", thinking=None,
|
| 1401 |
reasoning_effort=None, allow_fallback=None, response_format=None,
|
| 1402 |
+
logical_node=None,
|
| 1403 |
):
|
| 1404 |
return self.call_model(
|
| 1405 |
system_prompt, user_prompt, max_tokens=max_tokens, temp=temp,
|
| 1406 |
model_name=model_name, model_role=model_role, thinking=thinking,
|
| 1407 |
reasoning_effort=reasoning_effort, allow_fallback=allow_fallback,
|
| 1408 |
+
response_format=response_format, logical_node=logical_node,
|
| 1409 |
)
|
| 1410 |
|
| 1411 |
def stream_deepseek(
|
| 1412 |
self, system_prompt, user_prompt, max_tokens=8000, model_name=None,
|
| 1413 |
model_role="reasoning", thinking=None, reasoning_effort=None, temp=0.3,
|
| 1414 |
+
allow_fallback=None, reasoning_only_recovery=False, logical_node=None,
|
| 1415 |
):
|
| 1416 |
yield from self.stream_model(
|
| 1417 |
system_prompt, user_prompt, max_tokens=max_tokens, model_name=model_name,
|
| 1418 |
model_role=model_role, thinking=thinking, reasoning_effort=reasoning_effort,
|
| 1419 |
temp=temp, allow_fallback=allow_fallback,
|
| 1420 |
+
reasoning_only_recovery=reasoning_only_recovery, logical_node=logical_node,
|
| 1421 |
)
|
| 1422 |
|
| 1423 |
def parse_user_core_literature(self, user_lit_text):
|
|
|
|
| 1439 |
model_role="reasoning",
|
| 1440 |
thinking=True,
|
| 1441 |
allow_fallback=False,
|
| 1442 |
+
logical_node='literature_parsing',
|
| 1443 |
)
|
| 1444 |
papers = self._extract_json_from_text(response)
|
| 1445 |
if not papers:
|
|
|
|
| 1503 |
user_prompt = APP_CONFIG["prompt_templates"]["synthesis_user"].replace(
|
| 1504 |
"{topic}", topic).replace("{user_injection}", user_injection).replace(
|
| 1505 |
"{refs_text}", refs_text).replace("{directive}", APP_CONFIG['deep_synthesis_directive'])
|
| 1506 |
+
return self.call_deepseek(sys_prompt, user_prompt, temp=0.2, logical_node="literature_synthesis")
|
| 1507 |
|
| 1508 |
def generate_anchors(
|
| 1509 |
self,
|
|
|
|
| 1546 |
model_role="reasoning",
|
| 1547 |
thinking=False,
|
| 1548 |
allow_fallback=False,
|
| 1549 |
+
logical_node='literature_synthesis',
|
| 1550 |
)
|
| 1551 |
|
| 1552 |
return _stream_direct_synthesis()
|
|
|
|
| 1605 |
model_role="reasoning",
|
| 1606 |
thinking=False,
|
| 1607 |
allow_fallback=False,
|
| 1608 |
+
logical_node='research_logic',
|
| 1609 |
)
|
| 1610 |
|
| 1611 |
return _stream_direct_research_logic()
|
|
|
|
| 1712 |
thinking=False,
|
| 1713 |
allow_fallback=False,
|
| 1714 |
response_format={"type": "json_object"},
|
| 1715 |
+
logical_node='front_matter_writing',
|
| 1716 |
)
|
| 1717 |
try:
|
| 1718 |
parsed = self._extract_json_from_text(response)
|
|
|
|
| 2675 |
user_prompt,
|
| 2676 |
temp=0.1 + attempt * 0.1,
|
| 2677 |
model_role="fast",
|
| 2678 |
+
logical_node='search_jit',
|
| 2679 |
))
|
| 2680 |
if queries and isinstance(queries, list) and len(queries) > 0:
|
| 2681 |
final_queries = [str(q).strip() for q in queries if self._is_english_dominant(str(q).strip())]
|
|
|
|
| 3128 |
f"Topic: {topic}\nKeywords: {keywords}\nUsed: {sorted(all_used_terms)}",
|
| 3129 |
temp=0.4,
|
| 3130 |
model_role='fast',
|
| 3131 |
+
logical_node='search_jit',
|
| 3132 |
)
|
| 3133 |
parsed = self._extract_json_from_text(response)
|
| 3134 |
terms = parsed if isinstance(parsed, list) else []
|
|
|
|
| 3334 |
model_role='reasoning',
|
| 3335 |
thinking=False,
|
| 3336 |
allow_fallback=False,
|
| 3337 |
+
logical_node='citation_planning',
|
| 3338 |
)
|
| 3339 |
parsed = self._extract_json_from_text(raw)
|
| 3340 |
parsed_rows = parsed.get('assignments') if isinstance(parsed, dict) else None
|
|
|
|
| 5291 |
model_role='reasoning',
|
| 5292 |
thinking=False,
|
| 5293 |
allow_fallback=False,
|
| 5294 |
+
logical_node='critical_citation_adjudication',
|
| 5295 |
)
|
| 5296 |
parsed = self._extract_json_from_text(response)
|
| 5297 |
if isinstance(parsed, dict):
|
|
|
|
| 5435 |
model_role="reasoning",
|
| 5436 |
thinking=False,
|
| 5437 |
allow_fallback=False,
|
| 5438 |
+
logical_node='ordinary_citation_qa',
|
| 5439 |
)
|
| 5440 |
parsed = self._extract_json_from_text(response)
|
| 5441 |
last_error = None
|
|
|
|
| 5489 |
model_role="reasoning",
|
| 5490 |
thinking=False,
|
| 5491 |
allow_fallback=False,
|
| 5492 |
+
logical_node='ordinary_citation_qa',
|
| 5493 |
)
|
| 5494 |
parsed = self._extract_json_from_text(response)
|
| 5495 |
by_id = index_items(parsed)
|
|
|
|
| 5962 |
model_role='writing',
|
| 5963 |
thinking=False,
|
| 5964 |
allow_fallback=False,
|
| 5965 |
+
logical_node='ordinary_repair',
|
| 5966 |
)
|
| 5967 |
revised = self._remove_ai_meta_traces(revised)
|
| 5968 |
original_numbers = self._numeric_tokens(value)
|
|
|
|
| 6015 |
raw = self.call_deepseek(
|
| 6016 |
'You are an independent senior management-journal reviewer. Be exact and conservative.',
|
| 6017 |
prompt, max_tokens=800, temp=0.0, model_role='fast', thinking=False, allow_fallback=True,
|
| 6018 |
+
logical_node='section_critique',
|
| 6019 |
)
|
| 6020 |
except PipelineCancelledError:
|
| 6021 |
raise
|
|
|
|
| 6090 |
repaired = self.call_model(
|
| 6091 |
'You are SmartPaper Adaptive Academic Repair. Preserve evidence truth and repair prose with Conciseness, Accuracy, and Rigor.',
|
| 6092 |
prompt, max_tokens=7000, temp=0.15, model_role='repair', thinking=True, allow_fallback=True,
|
| 6093 |
+
logical_node='ordinary_repair',
|
| 6094 |
)
|
| 6095 |
except PipelineCancelledError:
|
| 6096 |
raise
|
|
|
|
| 8113 |
json.dumps({'ir_version': self.CANONICAL_GLOSSARY_IR_VERSION, 'terms': payload}, ensure_ascii=False),
|
| 8114 |
max_tokens=max(1200, min(5000, 120 + len(payload) * 90)),
|
| 8115 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 8116 |
+
logical_node='translation',
|
| 8117 |
)
|
| 8118 |
parsed = self._extract_json_from_text(response)
|
| 8119 |
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
|
|
|
| 8376 |
json.dumps({'ir_version': self.CANONICAL_GLOSSARY_IR_VERSION, 'terms': payload}, ensure_ascii=False),
|
| 8377 |
max_tokens=max(1200, min(4200, 180 + len(payload) * 120)),
|
| 8378 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 8379 |
+
logical_node='ordinary_translation_qa',
|
| 8380 |
)
|
| 8381 |
parsed = self._extract_json_from_text(response)
|
| 8382 |
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
|
|
|
| 8509 |
json.dumps({'terms': missing}, ensure_ascii=False),
|
| 8510 |
max_tokens=max(1200, min(5000, len(missing) * 80)),
|
| 8511 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 8512 |
+
logical_node='translation',
|
| 8513 |
)
|
| 8514 |
parsed = self._extract_json_from_text(response)
|
| 8515 |
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
|
|
|
| 8657 |
response = self.call_deepseek(
|
| 8658 |
'You are a bilingual academic translation equivalence auditor. Be conservative and evidence-preserving.',
|
| 8659 |
prompt, max_tokens=1200, temp=0.0, model_role='reasoning', thinking=False, allow_fallback=False,
|
| 8660 |
+
logical_node='ordinary_translation_qa',
|
| 8661 |
)
|
| 8662 |
parsed = self._extract_json_from_text(response)
|
| 8663 |
ok = parsed.get('ok') if isinstance(parsed, dict) else None
|
|
|
|
| 8749 |
system_prompt, json.dumps(payload, ensure_ascii=False),
|
| 8750 |
max_tokens=max(1200, min(3600, 650 + 260 * len(rows))),
|
| 8751 |
temp=0.0, model_role='reasoning', thinking=False, allow_fallback=False,
|
| 8752 |
+
logical_node='ordinary_translation_qa',
|
| 8753 |
)
|
| 8754 |
parsed = self._extract_json_from_text(response)
|
| 8755 |
returned = parsed.get('items', []) if isinstance(parsed, dict) else []
|
|
|
|
| 9021 |
model_role='translation',
|
| 9022 |
thinking=False,
|
| 9023 |
allow_fallback=False,
|
| 9024 |
+
logical_node='translation',
|
| 9025 |
)
|
| 9026 |
parsed = self._extract_json_from_text(response)
|
| 9027 |
items = parsed.get('terms', []) if isinstance(parsed, dict) else []
|
|
|
|
| 9211 |
effective_prompt, marked_source,
|
| 9212 |
max_tokens=max(1800, min(9000, self._count_tokens(marked_source) * 2 + 1200)),
|
| 9213 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 9214 |
+
logical_node='translation',
|
| 9215 |
)
|
| 9216 |
try:
|
| 9217 |
parsed = self._parse_marked_translation(result, expected_ids)
|
|
|
|
| 9397 |
response = self.call_deepseek(
|
| 9398 |
prompt, json.dumps(payload, ensure_ascii=False), max_tokens=900, temp=0.0,
|
| 9399 |
model_role='reasoning', thinking=False, allow_fallback=False,
|
| 9400 |
+
logical_node='ordinary_translation_qa',
|
| 9401 |
)
|
| 9402 |
parsed = self._extract_json_from_text(response)
|
| 9403 |
raw = parsed.get('delta_codes', []) if isinstance(parsed, dict) else []
|
|
|
|
| 9494 |
effective_prompt, marked_source,
|
| 9495 |
max_tokens=max(1200, min(2600, self._count_tokens(marked_source) * 2 + 900)),
|
| 9496 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 9497 |
+
logical_node='translation',
|
| 9498 |
)
|
| 9499 |
parsed = self._parse_marked_translation(result, [block_id])
|
| 9500 |
try:
|
|
|
|
| 9521 |
strict_prompt, marked_source,
|
| 9522 |
max_tokens=max(1200, min(2600, self._count_tokens(marked_source) * 2 + 900)),
|
| 9523 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 9524 |
+
logical_node='translation',
|
| 9525 |
)
|
| 9526 |
strict_parsed = self._parse_marked_translation(strict_result, [block_id])
|
| 9527 |
restored = self._restore_translation_tokens(
|
|
|
|
| 9547 |
numeric_prompt, marked_source,
|
| 9548 |
max_tokens=max(1200, min(2600, self._count_tokens(marked_source) * 2 + 900)),
|
| 9549 |
temp=0.0, model_role='translation', thinking=False, allow_fallback=False,
|
| 9550 |
+
logical_node='translation',
|
| 9551 |
)
|
| 9552 |
retry_parsed = self._parse_marked_translation(retry_result, [block_id])
|
| 9553 |
restored = self._restore_translation_tokens(
|
|
|
|
| 9921 |
system_prompt, json.dumps(payload, ensure_ascii=False),
|
| 9922 |
max_tokens=max(1200, min(3200, 600 + 180 * len(model_rows))),
|
| 9923 |
temp=0.0, model_role='reasoning', thinking=False, allow_fallback=False,
|
| 9924 |
+
logical_node='critical_translation_adjudication',
|
| 9925 |
)
|
| 9926 |
parsed = self._extract_json_from_text(response)
|
| 9927 |
returned = parsed.get('items', []) if isinstance(parsed, dict) else []
|
|
|
|
| 10686 |
translated = self.call_model(
|
| 10687 |
'You are SmartPaper Adaptive Translation. Produce faithful academic translation with no added or omitted claims.',
|
| 10688 |
prompt, max_tokens=8000, temp=0.1, model_role='translation', thinking=False, allow_fallback=True,
|
| 10689 |
+
logical_node='translation',
|
| 10690 |
)
|
| 10691 |
translated = re.sub(r'<think>.*?</think>', '', str(translated or ''), flags=re.S).strip()
|
| 10692 |
if not translated:
|
core/qualitative_engine.py
CHANGED
|
@@ -733,6 +733,7 @@ Rules:
|
|
| 733 |
base_prompt,
|
| 734 |
max_tokens=8000, temp=0.0, model_role="reasoning",
|
| 735 |
thinking=False, allow_fallback=False, response_format={"type": "json_object"},
|
|
|
|
| 736 |
)
|
| 737 |
else:
|
| 738 |
retry_prompt = base_prompt + (
|
|
@@ -753,6 +754,7 @@ Rules:
|
|
| 753 |
thinking=False,
|
| 754 |
allow_fallback=False,
|
| 755 |
response_format={"type": "json_object"},
|
|
|
|
| 756 |
)
|
| 757 |
|
| 758 |
response = re.sub(
|
|
|
|
| 733 |
base_prompt,
|
| 734 |
max_tokens=8000, temp=0.0, model_role="reasoning",
|
| 735 |
thinking=False, allow_fallback=False, response_format={"type": "json_object"},
|
| 736 |
+
logical_node='structured_json',
|
| 737 |
)
|
| 738 |
else:
|
| 739 |
retry_prompt = base_prompt + (
|
|
|
|
| 754 |
thinking=False,
|
| 755 |
allow_fallback=False,
|
| 756 |
response_format={"type": "json_object"},
|
| 757 |
+
logical_node='structured_json',
|
| 758 |
)
|
| 759 |
|
| 760 |
response = re.sub(
|
core/quantitative_engine.py
CHANGED
|
@@ -480,6 +480,7 @@ Keywords: {keywords}"""
|
|
| 480 |
model_role="fast",
|
| 481 |
thinking=False,
|
| 482 |
allow_fallback=True,
|
|
|
|
| 483 |
)
|
| 484 |
parsed = agent._extract_json_from_text(raw)
|
| 485 |
if not isinstance(parsed, dict):
|
|
|
|
| 480 |
model_role="fast",
|
| 481 |
thinking=False,
|
| 482 |
allow_fallback=True,
|
| 483 |
+
logical_node='structured_json',
|
| 484 |
)
|
| 485 |
parsed = agent._extract_json_from_text(raw)
|
| 486 |
if not isinstance(parsed, dict):
|