U1-R2C Two-Design Research UI Simplification

#128
Files changed (3) hide show
  1. app.py +27 -21
  2. core/advanced_method_registry.py +92 -116
  3. i18n.py +21 -83
app.py CHANGED
@@ -795,11 +795,7 @@ def _process_pending_launch(lang_code: str) -> None:
795
  ):
796
  st.session_state["sp_pending_launch"] = None
797
  reason = str(preflight.get("block_reason") or "method_preflight_changed")
798
- if reason == "method_capability_blocked":
799
- method_label = tr(lang_code, f"method_{resolved_family}")
800
- st.warning(tr(lang_code, "method_not_supported", method=method_label))
801
- else:
802
- st.warning(tr(lang_code, "method_resolution_invalid"))
803
  return
804
 
805
  projection = get_product_ui_projection()
@@ -1506,24 +1502,30 @@ def render_workspace_controls(user):
1506
  }
1507
  method_signature = ""
1508
  else:
1509
- # U1-R2B: Title + Keywords determine the automatic design. If no design
1510
- # cue is present, the resolver defaults to qualitative. The selector is
1511
- # always visible for a new task and may override the automatic/default
1512
- # choice, but the capability gate still runs afterwards.
 
1513
  auto_preflight, method_signature = _current_method_preflight(confirmed_method_family="")
1514
  auto_family = str(auto_preflight.get("method_family") or "qualitative").strip().casefold()
1515
  if auto_family not in METHOD_FAMILIES:
1516
  auto_family = "qualitative"
 
1517
 
1518
  stored = str(st.session_state.get("sp_method_confirmation") or "").strip().casefold()
1519
  stored_sig = str(st.session_state.get("sp_method_confirmation_signature") or "").strip()
1520
- selected_default = stored if stored_sig == method_signature and stored in METHOD_FAMILIES else auto_family
 
 
 
 
1521
 
1522
  # A changed Title/Keywords signature invalidates a previous manual
1523
- # override and resets the visible selector to the fresh auto/default
1524
- # result. This prevents stale method choices leaking across topics.
1525
  selector_sig = str(st.session_state.get("sp_method_selector_signature") or "").strip()
1526
- if selector_sig != method_signature:
1527
  st.session_state["sp_method_confirmation"] = ""
1528
  st.session_state["sp_method_confirmation_signature"] = ""
1529
  st.session_state["sp_method_selector"] = auto_family
@@ -1538,10 +1540,14 @@ def render_workspace_controls(user):
1538
  index=list(METHOD_FAMILIES).index(selected_default),
1539
  format_func=lambda family: tr(lang_code, f"method_{family}"),
1540
  key="sp_method_selector",
1541
- disabled=active,
1542
  )
1543
  selected = str(selected or auto_family).strip().casefold()
1544
- if selected != auto_family:
 
 
 
 
1545
  st.session_state["sp_method_confirmation"] = selected
1546
  st.session_state["sp_method_confirmation_signature"] = method_signature
1547
  else:
@@ -1560,8 +1566,11 @@ def render_workspace_controls(user):
1560
  st.caption(tr(lang_code, "method_detection_explanation"))
1561
 
1562
  block_reason = str(preflight.get("block_reason") or "")
1563
- if block_reason == "method_capability_blocked":
1564
- st.warning(tr(lang_code, "method_not_supported", method=method_label))
 
 
 
1565
  elif not active:
1566
  st.info(tr(lang_code, "method_supported", method=method_label))
1567
 
@@ -1655,10 +1664,7 @@ def render_workspace_controls(user):
1655
  )
1656
  if not bool(click_preflight.get("can_start_generation")):
1657
  reason = str(click_preflight.get("block_reason") or "")
1658
- if reason == "method_capability_blocked":
1659
- st.warning(tr(lang_code, "method_not_supported", method=method_label))
1660
- else:
1661
- st.warning(tr(lang_code, "method_resolution_invalid"))
1662
  else:
1663
  resolved_family = str(click_preflight.get("method_family") or "").strip().casefold()
1664
  resolved_mode = str(click_preflight.get("legacy_policy_lane") or "").strip().lower()
 
795
  ):
796
  st.session_state["sp_pending_launch"] = None
797
  reason = str(preflight.get("block_reason") or "method_preflight_changed")
798
+ st.warning(tr(lang_code, "method_resolution_invalid"))
 
 
 
 
799
  return
800
 
801
  projection = get_product_ui_projection()
 
1502
  }
1503
  method_signature = ""
1504
  else:
1505
+ # U1-R2C: SmartPaper intentionally supports only two reader-visible
1506
+ # research designs. No cue defaults to qualitative. Specialized-method
1507
+ # cues (panel/meta/fsQCA/bibliometric-review) are advisory-only and pin
1508
+ # the current topic to the qualitative framework rather than exposing
1509
+ # or blocking on an unsupported method family.
1510
  auto_preflight, method_signature = _current_method_preflight(confirmed_method_family="")
1511
  auto_family = str(auto_preflight.get("method_family") or "qualitative").strip().casefold()
1512
  if auto_family not in METHOD_FAMILIES:
1513
  auto_family = "qualitative"
1514
+ specialized_redirect = bool((auto_preflight.get("resolution") or {}).get("specialized_method_detected"))
1515
 
1516
  stored = str(st.session_state.get("sp_method_confirmation") or "").strip().casefold()
1517
  stored_sig = str(st.session_state.get("sp_method_confirmation_signature") or "").strip()
1518
+ selected_default = (
1519
+ auto_family
1520
+ if specialized_redirect
1521
+ else (stored if stored_sig == method_signature and stored in METHOD_FAMILIES else auto_family)
1522
+ )
1523
 
1524
  # A changed Title/Keywords signature invalidates a previous manual
1525
+ # override. Specialized topics also clear any prior override so they
1526
+ # cannot be silently converted into an ordinary survey manuscript.
1527
  selector_sig = str(st.session_state.get("sp_method_selector_signature") or "").strip()
1528
+ if selector_sig != method_signature or specialized_redirect:
1529
  st.session_state["sp_method_confirmation"] = ""
1530
  st.session_state["sp_method_confirmation_signature"] = ""
1531
  st.session_state["sp_method_selector"] = auto_family
 
1540
  index=list(METHOD_FAMILIES).index(selected_default),
1541
  format_func=lambda family: tr(lang_code, f"method_{family}"),
1542
  key="sp_method_selector",
1543
+ disabled=active or specialized_redirect,
1544
  )
1545
  selected = str(selected or auto_family).strip().casefold()
1546
+ if specialized_redirect:
1547
+ selected = "qualitative"
1548
+ st.session_state["sp_method_confirmation"] = ""
1549
+ st.session_state["sp_method_confirmation_signature"] = ""
1550
+ elif selected != auto_family:
1551
  st.session_state["sp_method_confirmation"] = selected
1552
  st.session_state["sp_method_confirmation_signature"] = method_signature
1553
  else:
 
1566
  st.caption(tr(lang_code, "method_detection_explanation"))
1567
 
1568
  block_reason = str(preflight.get("block_reason") or "")
1569
+ specialized_redirect = bool((preflight.get("resolution") or {}).get("specialized_method_detected"))
1570
+ if specialized_redirect and not active:
1571
+ st.warning(tr(lang_code, "specialized_method_redirect"))
1572
+ elif block_reason:
1573
+ st.warning(tr(lang_code, "method_resolution_invalid"))
1574
  elif not active:
1575
  st.info(tr(lang_code, "method_supported", method=method_label))
1576
 
 
1664
  )
1665
  if not bool(click_preflight.get("can_start_generation")):
1666
  reason = str(click_preflight.get("block_reason") or "")
1667
+ st.warning(tr(lang_code, "method_resolution_invalid"))
 
 
 
1668
  else:
1669
  resolved_family = str(click_preflight.get("method_family") or "").strip().casefold()
1670
  resolved_mode = str(click_preflight.get("legacy_policy_lane") or "").strip().lower()
core/advanced_method_registry.py CHANGED
@@ -1,13 +1,14 @@
1
  # -*- coding: utf-8 -*-
2
- """Deterministic research-design resolver and capability preflight for SmartPaper.
3
 
4
- U1-R2B derives an academic ``method_family`` from Title + Keywords only, maps
5
- that family to the legacy AMJ/ASQ policy lane, and blocks unsupported method
6
- families before task/quota creation. When no design cue is detected, the
7
- resolver defaults to qualitative research. A user's manual design adjustment
8
- is authoritative for the current Title + Keywords signature, but it never
9
- bypasses the capability gate. This module never selects providers/models and
10
- never rewrites Title/Keywords.
 
11
  """
12
  from __future__ import annotations
13
 
@@ -15,61 +16,34 @@ import os
15
  import re
16
  from typing import Any, Iterable
17
 
18
- METHOD_RESOLVER_VERSION = "smartpaper-auto-method-resolver-v3"
19
- METHOD_CAPABILITY_REGISTRY_VERSION = "smartpaper-method-capability-registry-v2"
20
 
 
 
21
  METHOD_FAMILIES = (
22
- "survey_path",
23
  "qualitative",
24
- "fsqca",
25
- "meta_analysis",
26
- "panel_econometrics",
27
- "bibliometric_integrative_review",
28
  )
29
 
30
- # Capability status is executable U1-R2 preflight authority. Advanced methods
31
- # remain BLOCKED until their dedicated engines are implemented.
32
  _METHOD_CAPABILITIES: dict[str, dict[str, Any]] = {
33
- "survey_path": {
34
- "legacy_policy_lane": "amj",
35
- "capability_status": "production_supported",
36
- "evidence_timing": "pre_literature",
37
- "engine_family": "quantitative_engine",
38
- },
39
  "qualitative": {
40
  "legacy_policy_lane": "asq",
41
  "capability_status": "production_supported",
42
  "evidence_timing": "pre_literature",
43
  "engine_family": "qualitative_engine",
44
  },
45
- "fsqca": {
46
- "legacy_policy_lane": "amj",
47
- "capability_status": "blocked_pending_engine",
48
- "evidence_timing": "pre_literature",
49
- "engine_family": "fsqca_engine_pending",
50
- },
51
- "meta_analysis": {
52
- "legacy_policy_lane": "amj",
53
- "capability_status": "blocked_pending_engine",
54
- "evidence_timing": "post_literature",
55
- "engine_family": "meta_analysis_engine_pending",
56
- },
57
- "panel_econometrics": {
58
  "legacy_policy_lane": "amj",
59
- "capability_status": "blocked_pending_engine",
60
  "evidence_timing": "pre_literature",
61
- "engine_family": "panel_econometrics_engine_pending",
62
- },
63
- "bibliometric_integrative_review": {
64
- "legacy_policy_lane": "amj",
65
- "capability_status": "blocked_pending_engine",
66
- "evidence_timing": "post_literature",
67
- "engine_family": "review_engine_pending",
68
  },
69
  }
70
 
71
- # Priority matters. Method-defining paradigms outrank generic survey cues.
72
- _METHOD_PATTERNS: tuple[tuple[str, tuple[str, ...]], ...] = (
 
73
  (
74
  "fsqca",
75
  (
@@ -84,19 +58,7 @@ _METHOD_PATTERNS: tuple[tuple[str, tuple[str, ...]], ...] = (
84
  "meta_analysis",
85
  (
86
  r"\bmeta[- ]analysis\b", r"\bmeta[- ]analytic\b", r"\bmeta analysis\b",
87
- r"元分析", r"荟萃分析",
88
- r"мета[- ]?анализ", r"метааналитическ",
89
- r"мета[- ]?талдау", r"метааналитикалық",
90
- ),
91
- ),
92
- (
93
- "bibliometric_integrative_review",
94
- (
95
- r"\bbibliometric\b", r"\bscience\s+mapping\b", r"\bco[- ]citation\b", r"\bco[- ]word\b",
96
- r"\bintegrative\s+(?:theory\s+)?review\b", r"\btheory\s+review\b", r"\bsystematic\s+literature\s+review\b",
97
- r"文献计量", r"科学知识图谱", r"共被引", r"共词", r"整合性(?:理论)?综述", r"系统性文献综述",
98
- r"библиометр", r"научн\w*.{0,8}картирован", r"коцитирован", r"интегративн\w*.{0,8}обзор",
99
- r"библиометр", r"ғылыми.{0,8}кар��алау", r"бірлескен.{0,8}дәйексөз", r"интегративті.{0,8}шолу",
100
  ),
101
  ),
102
  (
@@ -105,10 +67,22 @@ _METHOD_PATTERNS: tuple[tuple[str, tuple[str, ...]], ...] = (
105
  r"\bpanel\s+data\b", r"\bfirm[- ]year\b", r"\bentity\s+fixed\s+effects?\b", r"\bfixed\s+effects?\b",
106
  r"\brandom\s+effects?\b", r"\bpanel\s+econometrics?\b", r"\btwo[- ]way\s+fixed\s+effects?\b",
107
  r"面板数据", r"固定效应", r"双向固定效应", r"公司[-—– ]?年度",
108
- r"панельн\w*.{0,8}данн", r"фиксированн\w*.{0,8}эффект",
109
- r"панельдік.{0,8}дерек", r"тұрақты.{0,8}әсер",
 
 
 
 
 
 
 
 
 
110
  ),
111
  ),
 
 
 
112
  (
113
  "qualitative",
114
  (
@@ -132,14 +106,6 @@ _METHOD_PATTERNS: tuple[tuple[str, tuple[str, ...]], ...] = (
132
  ),
133
  )
134
 
135
- _MATERIAL_NAME_PATTERNS: tuple[tuple[str, tuple[str, ...]], ...] = (
136
- ("fsqca", (r"fsqca", r"truth[-_ ]?table", r"calibrat", r"condition[-_ ]?outcome")),
137
- ("meta_analysis", (r"meta[-_ ]?analysis", r"effect[-_ ]?size", r"forest[-_ ]?plot", r"study[-_ ]?character")),
138
- ("bibliometric_integrative_review", (r"bibliometric", r"corpus", r"co[-_ ]?citation", r"co[-_ ]?word", r"openalex")),
139
- ("panel_econometrics", (r"panel", r"firm[-_ ]?year", r"entity[-_ ]?year", r"fixed[-_ ]?effect")),
140
- ("qualitative", (r"interview", r"transcript", r"field[-_ ]?note", r"focus[-_ ]?group", r"访谈", r"сұхбат", r"интервью")),
141
- )
142
-
143
 
144
  def _normalized_text(value: Any) -> str:
145
  return re.sub(r"\s+", " ", str(value or "")).strip()
@@ -175,7 +141,7 @@ def _material_descriptors(materials: Iterable[Any] | None) -> list[dict[str, str
175
 
176
 
177
  def method_capability(method_family: str) -> dict[str, Any]:
178
- """Return a copy of versioned capability metadata for one method family."""
179
  family = str(method_family or "").strip().casefold()
180
  row = dict(_METHOD_CAPABILITIES.get(family) or {})
181
  if not row:
@@ -194,45 +160,57 @@ def method_capability(method_family: str) -> dict[str, Any]:
194
  return row
195
 
196
 
 
 
 
 
 
 
 
 
 
197
  def resolve_method_family(
198
  title: Any,
199
  keywords: Any,
200
  *,
201
  materials: Iterable[Any] | None = None,
202
  ) -> dict[str, Any]:
203
- """Resolve one academic research-design family from Title + Keywords.
204
 
205
- ``materials`` remains in the public signature for backward compatibility,
206
- but U1-R2B deliberately does not use uploaded material names or contents to
207
- infer the design. If no explicit design cue is found, qualitative research
208
- is selected as the non-blocking default.
209
  """
210
  source_title = _normalized_text(title)
211
  source_keywords = _keyword_items(keywords)
212
  text = " ; ".join([source_title] + source_keywords)
213
-
214
- hits: list[dict[str, Any]] = []
215
- chosen = ""
216
- for family, patterns in _METHOD_PATTERNS:
217
- matched = [pattern for pattern in patterns if re.search(pattern, text, flags=re.I | re.UNICODE)]
218
- if matched:
219
- hits.append({"method_family": family, "source": "title_keywords", "match_count": len(matched)})
220
- if not chosen:
221
- chosen = family
222
-
223
  descriptors = _material_descriptors(materials)
224
 
225
- confidence = "AUTO_DETECTED" if chosen else "DEFAULT_QUALITATIVE"
226
- reason_codes: list[str] = []
227
- if chosen:
228
- reason_codes.append("explicit_title_keyword_method_cue")
229
- conflicting = [row["method_family"] for row in hits if row["method_family"] != chosen]
230
- if conflicting:
231
- reason_codes.append("lower_priority_method_cues_present")
232
- else:
233
- chosen = "qualitative"
234
- reason_codes.append("no_explicit_method_cue")
235
- reason_codes.append("default_qualitative")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
  capability = method_capability(chosen)
238
  return {
@@ -241,7 +219,10 @@ def resolve_method_family(
241
  "method_family": chosen,
242
  "confidence": confidence,
243
  "reason_codes": reason_codes,
244
- "title_keyword_hits": hits,
 
 
 
245
  "material_hint_families": [],
246
  "material_count": len(descriptors),
247
  "derived_policy_lane": capability.get("legacy_policy_lane"),
@@ -250,7 +231,7 @@ def resolve_method_family(
250
 
251
 
252
  def compare_shadow_to_legacy_lane(resolution: dict[str, Any], selected_lane: Any) -> dict[str, Any]:
253
- """Compare shadow inference with the legacy user-selected AMJ/ASQ lane."""
254
  selected = str(selected_lane or "").strip().casefold()
255
  derived = str((resolution or {}).get("derived_policy_lane") or "").strip().casefold()
256
  return {
@@ -268,18 +249,19 @@ def resolve_generation_preflight(
268
  materials: Iterable[Any] | None = None,
269
  confirmed_method_family: Any = None,
270
  ) -> dict[str, Any]:
271
- """Resolve method + capability before any task/quota creation.
272
 
273
- A valid manual selection may override either an auto-detected design or the
274
- qualitative fallback. Capability validation always runs after the manual
275
- selection, so an unsupported advanced method still cannot create a task or
276
- reserve quota.
277
  """
278
  resolution = resolve_method_family(title, keywords, materials=materials)
279
- family = str(resolution.get("method_family") or "").strip().casefold()
280
 
281
  confirmed = str(confirmed_method_family or "").strip().casefold()
282
- if confirmed in METHOD_FAMILIES and confirmed != family:
 
283
  family = confirmed
284
  capability = method_capability(family)
285
  resolution = dict(resolution)
@@ -290,28 +272,22 @@ def resolve_generation_preflight(
290
  "capability": capability,
291
  "reason_codes": list(resolution.get("reason_codes") or []) + ["user_override_method_family"],
292
  })
 
 
 
293
 
294
  capability = method_capability(family)
295
  lane = str(capability.get("legacy_policy_lane") or "").strip().casefold() or None
296
  status = str(capability.get("capability_status") or "").strip().casefold()
297
- if family not in METHOD_FAMILIES or not lane:
298
  return {
299
  "can_start_generation": False,
300
  "block_reason": "method_resolution_invalid",
301
  "resolution": resolution,
302
- "method_family": family or "undetermined",
303
  "legacy_policy_lane": None,
304
  "capability": capability,
305
  }
306
- if status != "production_supported":
307
- return {
308
- "can_start_generation": False,
309
- "block_reason": "method_capability_blocked",
310
- "resolution": resolution,
311
- "method_family": family,
312
- "legacy_policy_lane": lane,
313
- "capability": capability,
314
- }
315
  return {
316
  "can_start_generation": True,
317
  "block_reason": "",
 
1
  # -*- coding: utf-8 -*-
2
+ """Deterministic two-design resolver and capability preflight for SmartPaper.
3
 
4
+ U1-R2C intentionally limits production research design to two supported paths:
5
+ qualitative research and survey-based quantitative research. Specialized cues
6
+ such as fsQCA, panel data/econometrics, meta-analysis, and bibliometric/review
7
+ methods are detected only to provide a reader-facing scope notice; they are not
8
+ exposed as selectable method families and are redirected to the qualitative
9
+ framework before task creation.
10
+
11
+ This module never selects providers/models and never rewrites Title/Keywords.
12
  """
13
  from __future__ import annotations
14
 
 
16
  import re
17
  from typing import Any, Iterable
18
 
19
+ METHOD_RESOLVER_VERSION = "smartpaper-auto-method-resolver-v4"
20
+ METHOD_CAPABILITY_REGISTRY_VERSION = "smartpaper-method-capability-registry-v3"
21
 
22
+ # Reader-visible / task-owning method families. Order is intentional:
23
+ # qualitative is the product default and appears first in the selector.
24
  METHOD_FAMILIES = (
 
25
  "qualitative",
26
+ "survey_path",
 
 
 
27
  )
28
 
 
 
29
  _METHOD_CAPABILITIES: dict[str, dict[str, Any]] = {
 
 
 
 
 
 
30
  "qualitative": {
31
  "legacy_policy_lane": "asq",
32
  "capability_status": "production_supported",
33
  "evidence_timing": "pre_literature",
34
  "engine_family": "qualitative_engine",
35
  },
36
+ "survey_path": {
 
 
 
 
 
 
 
 
 
 
 
 
37
  "legacy_policy_lane": "amj",
38
+ "capability_status": "production_supported",
39
  "evidence_timing": "pre_literature",
40
+ "engine_family": "quantitative_engine",
 
 
 
 
 
 
41
  },
42
  }
43
 
44
+ # Specialized methods are deliberately NOT method families in U1-R2C. They
45
+ # are scope cues only. A hit triggers a notice and a qualitative fallback.
46
+ _SPECIALIZED_METHOD_PATTERNS: tuple[tuple[str, tuple[str, ...]], ...] = (
47
  (
48
  "fsqca",
49
  (
 
58
  "meta_analysis",
59
  (
60
  r"\bmeta[- ]analysis\b", r"\bmeta[- ]analytic\b", r"\bmeta analysis\b",
61
+ r"元分析", r"荟萃分析", r"мета[- ]?анализ", r"метааналитическ", r"мета[- ]?талдау", r"метааналитикалық",
 
 
 
 
 
 
 
 
 
 
 
 
62
  ),
63
  ),
64
  (
 
67
  r"\bpanel\s+data\b", r"\bfirm[- ]year\b", r"\bentity\s+fixed\s+effects?\b", r"\bfixed\s+effects?\b",
68
  r"\brandom\s+effects?\b", r"\bpanel\s+econometrics?\b", r"\btwo[- ]way\s+fixed\s+effects?\b",
69
  r"面板数据", r"固定效应", r"双向固定效应", r"公司[-—– ]?年度",
70
+ r"панельн\w*.{0,8}данн", r"фиксированн\w*.{0,8}эффект", r"панельдік.{0,8}дерек", r"тұрақты.{0,8}әсер",
71
+ ),
72
+ ),
73
+ (
74
+ "bibliometric_review",
75
+ (
76
+ r"\bbibliometric\b", r"\bscience\s+mapping\b", r"\bco[- ]citation\b", r"\bco[- ]word\b",
77
+ r"\bintegrative\s+(?:theory\s+)?review\b", r"\btheory\s+review\b", r"\bsystematic\s+literature\s+review\b",
78
+ r"文献计量", r"科学知识图谱", r"共被引", r"共词", r"整合性(?:理论)?综述", r"系统性文献综述", r"文献综述",
79
+ r"библиометр", r"научн\w*.{0,8}картирован", r"коцитирован", r"интегративн\w*.{0,8}обзор", r"систематическ\w*.{0,8}обзор",
80
+ r"библиометр", r"ғылыми.{0,8}карталау", r"бірлескен.{0,8}дәйексөз", r"интегративті.{0,8}шолу", r"жүйелі.{0,8}әдебиет.{0,8}шолу",
81
  ),
82
  ),
83
+ )
84
+
85
+ _SUPPORTED_METHOD_PATTERNS: tuple[tuple[str, tuple[str, ...]], ...] = (
86
  (
87
  "qualitative",
88
  (
 
106
  ),
107
  )
108
 
 
 
 
 
 
 
 
 
109
 
110
  def _normalized_text(value: Any) -> str:
111
  return re.sub(r"\s+", " ", str(value or "")).strip()
 
141
 
142
 
143
  def method_capability(method_family: str) -> dict[str, Any]:
144
+ """Return a copy of versioned capability metadata for one supported family."""
145
  family = str(method_family or "").strip().casefold()
146
  row = dict(_METHOD_CAPABILITIES.get(family) or {})
147
  if not row:
 
160
  return row
161
 
162
 
163
+ def _pattern_hits(text: str, pattern_rows: tuple[tuple[str, tuple[str, ...]], ...]) -> list[dict[str, Any]]:
164
+ hits: list[dict[str, Any]] = []
165
+ for family, patterns in pattern_rows:
166
+ matched = [pattern for pattern in patterns if re.search(pattern, text, flags=re.I | re.UNICODE)]
167
+ if matched:
168
+ hits.append({"method_family": family, "source": "title_keywords", "match_count": len(matched)})
169
+ return hits
170
+
171
+
172
  def resolve_method_family(
173
  title: Any,
174
  keywords: Any,
175
  *,
176
  materials: Iterable[Any] | None = None,
177
  ) -> dict[str, Any]:
178
+ """Resolve one of SmartPaper's two supported production research designs.
179
 
180
+ Title + Keywords remain the design authority. Uploaded Research Materials
181
+ do not alter automatic design selection. Specialized-method cues are kept
182
+ only as an advisory classification and redirect to qualitative research.
 
183
  """
184
  source_title = _normalized_text(title)
185
  source_keywords = _keyword_items(keywords)
186
  text = " ; ".join([source_title] + source_keywords)
 
 
 
 
 
 
 
 
 
 
187
  descriptors = _material_descriptors(materials)
188
 
189
+ specialized_hits = _pattern_hits(text, _SPECIALIZED_METHOD_PATTERNS)
190
+ if specialized_hits:
191
+ capability = method_capability("qualitative")
192
+ return {
193
+ "resolver_version": METHOD_RESOLVER_VERSION,
194
+ "shadow_only": False,
195
+ "method_family": "qualitative",
196
+ "confidence": "SPECIALIZED_METHOD_REDIRECT",
197
+ "reason_codes": ["specialized_method_cue_detected", "redirect_to_qualitative"],
198
+ "title_keyword_hits": [],
199
+ "specialized_method_detected": True,
200
+ "specialized_method_families": [row["method_family"] for row in specialized_hits],
201
+ "specialized_method_hits": specialized_hits,
202
+ "material_hint_families": [],
203
+ "material_count": len(descriptors),
204
+ "derived_policy_lane": capability.get("legacy_policy_lane"),
205
+ "capability": capability,
206
+ }
207
+
208
+ supported_hits = _pattern_hits(text, _SUPPORTED_METHOD_PATTERNS)
209
+ chosen = str((supported_hits[0] if supported_hits else {}).get("method_family") or "qualitative")
210
+ confidence = "AUTO_DETECTED" if supported_hits else "DEFAULT_QUALITATIVE"
211
+ reason_codes = ["explicit_title_keyword_method_cue"] if supported_hits else ["no_explicit_method_cue", "default_qualitative"]
212
+ if len(supported_hits) > 1:
213
+ reason_codes.append("lower_priority_method_cues_present")
214
 
215
  capability = method_capability(chosen)
216
  return {
 
219
  "method_family": chosen,
220
  "confidence": confidence,
221
  "reason_codes": reason_codes,
222
+ "title_keyword_hits": supported_hits,
223
+ "specialized_method_detected": False,
224
+ "specialized_method_families": [],
225
+ "specialized_method_hits": [],
226
  "material_hint_families": [],
227
  "material_count": len(descriptors),
228
  "derived_policy_lane": capability.get("legacy_policy_lane"),
 
231
 
232
 
233
  def compare_shadow_to_legacy_lane(resolution: dict[str, Any], selected_lane: Any) -> dict[str, Any]:
234
+ """Compare inferred design with a legacy AMJ/ASQ lane."""
235
  selected = str(selected_lane or "").strip().casefold()
236
  derived = str((resolution or {}).get("derived_policy_lane") or "").strip().casefold()
237
  return {
 
249
  materials: Iterable[Any] | None = None,
250
  confirmed_method_family: Any = None,
251
  ) -> dict[str, Any]:
252
+ """Resolve the two supported designs before any task/quota creation.
253
 
254
+ Manual adjustment is available only between qualitative and survey-based
255
+ quantitative research. Specialized-method cues intentionally pin the
256
+ current topic to the qualitative fallback so a user cannot silently turn a
257
+ panel/meta/fsQCA/review topic into an ordinary survey manuscript.
258
  """
259
  resolution = resolve_method_family(title, keywords, materials=materials)
260
+ family = str(resolution.get("method_family") or "qualitative").strip().casefold()
261
 
262
  confirmed = str(confirmed_method_family or "").strip().casefold()
263
+ specialized = bool(resolution.get("specialized_method_detected"))
264
+ if not specialized and confirmed in METHOD_FAMILIES and confirmed != family:
265
  family = confirmed
266
  capability = method_capability(family)
267
  resolution = dict(resolution)
 
272
  "capability": capability,
273
  "reason_codes": list(resolution.get("reason_codes") or []) + ["user_override_method_family"],
274
  })
275
+ elif specialized:
276
+ resolution = dict(resolution)
277
+ resolution["manual_override_locked"] = True
278
 
279
  capability = method_capability(family)
280
  lane = str(capability.get("legacy_policy_lane") or "").strip().casefold() or None
281
  status = str(capability.get("capability_status") or "").strip().casefold()
282
+ if family not in METHOD_FAMILIES or not lane or status != "production_supported":
283
  return {
284
  "can_start_generation": False,
285
  "block_reason": "method_resolution_invalid",
286
  "resolution": resolution,
287
+ "method_family": family or "qualitative",
288
  "legacy_policy_lane": None,
289
  "capability": capability,
290
  }
 
 
 
 
 
 
 
 
 
291
  return {
292
  "can_start_generation": True,
293
  "block_reason": "",
i18n.py CHANGED
@@ -276,35 +276,27 @@ TEXTS: dict[str, dict[str, str]] = {
276
  "download_asq_template": "📥 一键下载 TXT 访谈模板",
277
  "start_asq": "🚀 启动 ASQ 模式生成",
278
  "research_materials_label": "研究资料(可选)",
279
- "research_materials_help": "支持 PDF、TXT、DOC、DOCX、CSV、XLS、XLSX。上传资料用于辅助论文生成;研究设计仅根据论文题目和关键词自动识别。",
280
  "drop_research_materials": "将研究资料拖到这里",
281
  "limit_research_materials": "每个文件最大 1 GB · PDF, TXT, DOC, DOCX, CSV, XLS, XLSX",
282
  "material_document_use_label": "如何使用上传的 PDF/DOC/DOCX 文件?",
283
  "material_use_reference": "作为参考文献资料",
284
  "material_use_research": "作为研究资料",
285
  "detected_method_label": "研究设计(自动识别,可调整)",
286
- "method_detection_explanation": "基于论文题目和关键词自动识别研究设计。未识别时默认质性研究,可手动调整。",
287
  "method_confirm_label": "调整研究设计",
288
  "method_confirm_placeholder": "请选择研究设计",
289
  "method_confirmation_required": "研究设计状态异常,请重新选择研究设计。",
290
  "method_resolution_invalid": "研究设计状态异常,请重新选择研究设计。",
291
- "method_not_supported": "当前研究设计为“{method}”。该研究设计的专用生成引擎尚未开放;本次不会创建任务,也不会占用额度。",
292
- "method_supported": "当前研究设计为“{method}”。系统将按对应研究流程生成论文。",
293
  "method_undetermined": "质性研究(默认)",
294
  "method_survey_path": "问卷量化研究",
295
  "method_qualitative": "质性研究",
296
- "method_fsqca": "模糊集定性比较分析(fsQCA)",
297
- "method_meta_analysis": "元分析",
298
- "method_panel_econometrics": "面板数据计量分析",
299
- "method_bibliometric_integrative_review": "文献计量与整合性综述",
300
  "adaptive_template_expander": "💡 推荐模板(可选)",
301
  "download_research_template": "📥 下载研究资料模板",
302
  "template_help_survey_path": "推荐使用变量或统计结果表。若未上传数据,系统会按项目规则生成用于初稿的辅助证据。",
303
  "template_help_qualitative": "推荐使用访谈、田野记录或案例材料。若未上传资料,系统会构建匿名的初稿证据包。",
304
- "template_help_fsqca": "推荐使用条件变量、结果变量及校准信息表。专用 fsQCA 引擎开放后将按此结构处理。",
305
- "template_help_meta_analysis": "推荐使用研究层面的效应量提取表。真实论文身份与系统辅助效应量必须严格分离。",
306
- "template_help_panel_econometrics": "推荐使用实体—时间面板数据表,例如企业—年度结构。",
307
- "template_help_bibliometric_integrative_review": "推荐使用真实文献语料的题名、作者、年份、DOI、期刊和关键词等元数据。",
308
  "template_help_undetermined": "确认研究设计后,系统将显示对应的研究资料模板。",
309
  "start_generation_unified": "🚀 开始生成",
310
  "api_key_missing": "❌ 缺少 DEEPSEEK_API_KEY,请配置API密钥。",
@@ -518,35 +510,27 @@ TEXTS: dict[str, dict[str, str]] = {
518
  "download_asq_template": "📥 Download TXT Interview Template",
519
  "start_asq": "🚀 Start ASQ Generation",
520
  "research_materials_label": "Research Materials (Optional)",
521
- "research_materials_help": "Supports PDF, TXT, DOC, DOCX, CSV, XLS, and XLSX. Uploaded files support paper generation; Research Design is identified from the Title and Keywords only.",
522
  "drop_research_materials": "Drag research materials here",
523
  "limit_research_materials": "Up to 1 GB per file · PDF, TXT, DOC, DOCX, CSV, XLS, XLSX",
524
  "material_document_use_label": "How should SmartPaper use the uploaded PDF/DOC/DOCX files?",
525
  "material_use_reference": "Use as reference literature",
526
  "material_use_research": "Use as research materials",
527
  "detected_method_label": "Research Design (Auto-detected, Adjustable)",
528
- "method_detection_explanation": "Automatically identifies the Research Design from the Title and Keywords. If none is identified, Qualitative Research is used by default. You can adjust it manually.",
529
  "method_confirm_label": "Adjust Research Design",
530
  "method_confirm_placeholder": "Select a Research Design",
531
  "method_confirmation_required": "The Research Design state is invalid. Please select a Research Design again.",
532
  "method_resolution_invalid": "The Research Design state is invalid. Please select a Research Design again.",
533
- "method_not_supported": "Current Research Design: {method}. Its dedicated generation engine is not yet enabled, so no task will be created and no quota will be used.",
534
- "method_supported": "Current Research Design: {method}. SmartPaper will use the corresponding research workflow.",
535
  "method_undetermined": "Qualitative Research (Default)",
536
  "method_survey_path": "Survey-Based Quantitative Research",
537
  "method_qualitative": "Qualitative Research",
538
- "method_fsqca": "Fuzzy-Set Qualitative Comparative Analysis (fsQCA)",
539
- "method_meta_analysis": "Meta-Analysis",
540
- "method_panel_econometrics": "Panel Data Econometric Analysis",
541
- "method_bibliometric_integrative_review": "Bibliometric Analysis and Integrative Literature Review",
542
  "adaptive_template_expander": "💡 Recommended Template (Optional)",
543
  "download_research_template": "📥 Download Research Materials Template",
544
  "template_help_survey_path": "A variable-level or statistical-results table is recommended. If no data are uploaded, SmartPaper may construct draft evidence under the project provenance rules.",
545
  "template_help_qualitative": "Interview, field-note, or case materials are recommended. If none are uploaded, SmartPaper may construct an anonymous draft evidence packet.",
546
- "template_help_fsqca": "A table of conditions, outcomes, and calibration information is recommended. The dedicated fsQCA engine will use this structure once enabled.",
547
- "template_help_meta_analysis": "A study-level effect-size extraction table is recommended. Real paper identities must remain separate from any system-assisted effect-size evidence.",
548
- "template_help_panel_econometrics": "An entity-time panel table, such as a firm-year structure, is recommended.",
549
- "template_help_bibliometric_integrative_review": "Use real-corpus metadata such as title, authors, year, DOI, venue, and keywords.",
550
  "template_help_undetermined": "Confirm the research design to view the corresponding research-material template.",
551
  "start_generation_unified": "🚀 Start Generation",
552
  "api_key_missing": "❌ DEEPSEEK_API_KEY is missing. Configure the API key.",
@@ -760,35 +744,27 @@ TEXTS: dict[str, dict[str, str]] = {
760
  "download_asq_template": "📥 Скачать TXT-шаблон интервью",
761
  "start_asq": "🚀 Запустить формирование в режиме ASQ",
762
  "research_materials_label": "Материалы исследования (необязательно)",
763
- "research_materials_help": "Поддерживаются PDF, TXT, DOC, DOCX, CSV, XLS и XLSX. Загруженные файлы используются как дополнительные исследовательские или справочные материалы; дизайн исследования определяется по названию статьи и ключевым словам.",
764
  "drop_research_materials": "Перетащите исследовательские материалы сюда",
765
  "limit_research_materials": "До 1 ГБ на файл · PDF, TXT, DOC, DOCX, CSV, XLS, XLSX",
766
  "material_document_use_label": "Как использовать загруженные файлы PDF/DOC/DOCX?",
767
  "material_use_reference": "Как научные источники",
768
  "material_use_research": "Как исследовательские материалы",
769
  "detected_method_label": "Дизайн исследования (определяется автоматически, можно изменить)",
770
- "method_detection_explanation": "Дизайн исследования определяется автоматически по названию статьи и ключевым словам. Если определить его не удаётся, по умолчанию выбирается качественное исследование. Выбор можно изменить вручную.",
771
  "method_confirm_label": "Изменить дизайн исследования",
772
  "method_confirm_placeholder": "Выберите дизайн исследования",
773
  "method_confirmation_required": "Некорректное состояние дизайна исследования. Выберите дизайн исследования повторно.",
774
  "method_resolution_invalid": "Некорректное состояние дизайна исследования. Выберите дизайн исследования повторно.",
775
- "method_not_supported": "Текущий дизайн исследования: «{method}». Специализированный движок для этого дизайна пока не включён, поэтому задача не будет создана и квота не будет списана.",
776
- "method_supported": "Текущий дизайн исследования: «{method}». SmartPaper применит соответствующий исследовательский процесс.",
777
  "method_undetermined": "Качественное исследование (по умолчанию)",
778
  "method_survey_path": "Количественное исследование на основе опроса",
779
  "method_qualitative": "Качественное исследование",
780
- "method_fsqca": "Качественный сравнительный анализ на основе нечётких множеств (fsQCA)",
781
- "method_meta_analysis": "Метаанализ",
782
- "method_panel_econometrics": "Эконометрический анализ панельных данных",
783
- "method_bibliometric_integrative_review": "Библиометрический анализ и интегративный обзор литературы",
784
  "adaptive_template_expander": "💡 Рекомендуемый шаблон (необязательно)",
785
  "download_research_template": "📥 Скачать шаблон исследовательских материалов",
786
  "template_help_survey_path": "Рекомендуется таблица переменных или статистических результатов. При отсутствии данных SmartPaper может сформировать черновые доказательные материалы в рамках правил происхождения данных.",
787
  "template_help_qualitative": "Рекомендуются интервью, полевые заметки или материалы кейсов. При их отсутствии SmartPaper может сформировать анонимный черновой пакет доказательств.",
788
- "template_help_fsqca": "Рекомендуется таблица условий, результата и параметров калибровки. После включения специализированный движок fsQCA будет использовать эту структуру.",
789
- "template_help_meta_analysis": "Рекомендуется таблица извлечения размеров эффектов на уровне исследований. Реальные публикации нельзя связывать с системно сформированными размерами эффектов.",
790
- "template_help_panel_econometrics": "Рекомендуется панельная таблица «объект–время», например структура «компания–год».",
791
- "template_help_bibliometric_integrative_review": "Используйте реальные метаданные корпуса: название, авторов, год, DOI, журнал и ключевые слова.",
792
  "template_help_undetermined": "После подтверждения дизайна исследования будет показан соответствующий шаблон материалов.",
793
  "start_generation_unified": "🚀 Начать",
794
  "api_key_missing": "❌ Не задан DEEPSEEK_API_KEY. Настройте ключ API.",
@@ -1002,35 +978,27 @@ TEXTS: dict[str, dict[str, str]] = {
1002
  "download_asq_template": "📥 TXT сұхбат үлгісін жүктеу",
1003
  "start_asq": "🚀 ASQ режимінде дайындауды бастау",
1004
  "research_materials_label": "Зерттеу материалдары (міндетті емес)",
1005
- "research_materials_help": "PDF, TXT, DOC, DOCX, CSV, XLS және XLSX форматтары қолдау табады. Жүктелген файлдар қосымша зерттеу немесе анықтамалық материал ретінде пайдаланылады; зерттеу дизайны мақала тақырыбы мен кілт сөздер бойынша анықталады.",
1006
  "drop_research_materials": "Зерттеу материалдарын осы жерге сүйреп әкеліңіз",
1007
  "limit_research_materials": "Әр файлға 1 ГБ дейін · PDF, TXT, DOC, DOCX, CSV, XLS, XLSX",
1008
  "material_document_use_label": "Жүктелген PDF/DOC/DOCX файлдарын қалай пайдалану керек?",
1009
  "material_use_reference": "Ғылыми дереккөз ретінде",
1010
  "material_use_research": "Зерттеу материалы ретінде",
1011
  "detected_method_label": "Зерттеу дизайны (автоматты түрде анықталады, өзгертуге болады)",
1012
- "method_detection_explanation": "Зерттеу дизайны мақала тақырыбы мен кілт сөздер негізінде автоматты түрде анықталады. Анықталмаса, әдепкі бойынша сапалық зерттеу таңдалады. Таңдауды қолмен өзгертуге болады.",
1013
  "method_confirm_label": "Зерттеу дизайнын өзгерту",
1014
  "method_confirm_placeholder": "Зерттеу дизайнын таңдаңыз",
1015
  "method_confirmation_required": "Зерттеу дизайнының күйі жарамсыз. Зерттеу дизайнын қайта таңдаңыз.",
1016
  "method_resolution_invalid": "Зерттеу дизайнының күйі жарамсыз. Зерттеу дизайнын қайта таңдаңыз.",
1017
- "method_not_supported": "Ағымдағы зерттеу дизайны: «{method}». Бұл дизайнның арнайы генерация қозғалтқышы әлі қосылмаған, сондықтан тапсырма құрылмайды және квота пайдаланылмайды.",
1018
- "method_supported": "Ағымдағы зерттеу дизайны: «{method}». SmartPaper тиісті зерттеу үдерісін қолданады.",
1019
  "method_undetermined": "Сапалық зерттеу (әдепкі)",
1020
  "method_survey_path": "Сауалнамаға негізделген сандық зерттеу",
1021
  "method_qualitative": "Сапалық зерттеу",
1022
- "method_fsqca": "Бұлыңғыр жиындарға негізделген сапалық салыстырмалы талдау (fsQCA)",
1023
- "method_meta_analysis": "Мета-талдау",
1024
- "method_panel_econometrics": "Панельдік деректерді эконометриялық талдау",
1025
- "method_bibliometric_integrative_review": "Библиометриялық талдау және әдебиеттерге интегративті шолу",
1026
  "adaptive_template_expander": "💡 Ұсынылатын үлгі (міндетті емес)",
1027
  "download_research_template": "📥 Зерттеу материалдары үлгісін жүктеу",
1028
  "template_help_survey_path": "Айнымалылар немесе статистикалық нәтижелер кестесі ұсынылады. Дерек жүктелмесе, SmartPaper жоба ережелеріне сәйкес бастапқы нұсқаға арналған көмекші дәлелдерді құра алады.",
1029
  "template_help_qualitative": "Сұхбат, далалық жазба немесе кейс материалдары ұсынылады. Олар болмаса, SmartPaper анонимді бастапқы дәлелдер пакетін құра алады.",
1030
- "template_help_fsqca": "Шарттар, нәтиже және калибрлеу ақпараты бар кесте ұсынылады. Арнайы fsQCA қозғалтқышы қосылғаннан кейін осы құрылым қолданылады.",
1031
- "template_help_meta_analysis": "Зерттеу деңгейіндегі әсер мөлшерлерін шығару кестесі ұсынылады. Нақты мақалалардың сәйкестендіргіштері жүйе құрастырған әсер мөлшерлерімен байланыстырылмауы тиіс.",
1032
- "template_help_panel_econometrics": "Нысан–уақыт панельдік кестесі, мысалы компания–жыл құрылымы ұсынылады.",
1033
- "template_help_bibliometric_integrative_review": "Нақты әдеби корпус метадеректерін пайдаланыңыз: атауы, авторлары, жылы, DOI, басылымы және түйін сөздері.",
1034
  "template_help_undetermined": "Зерттеу дизайнын растағаннан кейін тиісті материал үлгісі көрсетіледі.",
1035
  "start_generation_unified": "🚀 Бастау",
1036
  "api_key_missing": "❌ DEEPSEEK_API_KEY көрсетілмеген. API кілтін баптаңыз.",
@@ -1209,7 +1177,7 @@ def get_asq_txt_template(language: str) -> str:
1209
 
1210
 
1211
  def get_research_material_template(language: str, method_family: str) -> dict[str, str]:
1212
- """Return the method-adaptive user template without changing engine policy."""
1213
  family = str(method_family or "").strip().casefold()
1214
  if family == "survey_path":
1215
  return {
@@ -1218,41 +1186,11 @@ def get_research_material_template(language: str, method_family: str) -> dict[st
1218
  "filename": "Survey_Path_Data_Template.csv",
1219
  "mime_type": "text/csv",
1220
  }
1221
- if family == "qualitative":
1222
- return {
1223
- "content": get_asq_txt_template(language),
1224
- "code_language": "text",
1225
- "filename": "Qualitative_Research_Materials_Template.txt",
1226
- "mime_type": "text/plain",
1227
- }
1228
- advanced = {
1229
- "fsqca": (
1230
- "case_id,condition_1,condition_2,condition_3,outcome\n"
1231
- "CASE001,0.85,0.20,0.70,0.90\n",
1232
- "fsQCA_Conditions_Outcome_Template.csv",
1233
- ),
1234
- "meta_analysis": (
1235
- "study_id,effect_size,standard_error,sample_size,moderator\n"
1236
- "STUDY001,0.25,0.08,240,context_A\n",
1237
- "Meta_Analysis_Effect_Size_Template.csv",
1238
- ),
1239
- "panel_econometrics": (
1240
- "entity_id,year,outcome,x1,x2,control_1\n"
1241
- "E001,2025,0.62,0.40,0.71,0.15\n",
1242
- "Panel_Entity_Time_Template.csv",
1243
- ),
1244
- "bibliometric_integrative_review": (
1245
- "title,authors,year,doi,venue,keywords\n"
1246
- "Real publication title,Author A; Author B,2025,10.xxxx/xxxxx,Journal Name,keyword1; keyword2\n",
1247
- "Bibliometric_Corpus_Metadata_Template.csv",
1248
- ),
1249
- }
1250
- content, filename = advanced.get(family, ("", "SmartPaper_Research_Materials_Template.txt"))
1251
  return {
1252
- "content": content,
1253
- "code_language": "csv" if content else "text",
1254
- "filename": filename,
1255
- "mime_type": "text/csv" if content else "text/plain",
1256
  }
1257
 
1258
 
 
276
  "download_asq_template": "📥 一键下载 TXT 访谈模板",
277
  "start_asq": "🚀 启动 ASQ 模式生成",
278
  "research_materials_label": "研究资料(可选)",
279
+ "research_materials_help": "支持 PDF、TXT、DOC、DOCX、CSV、XLS、XLSX。上传资料用于辅助论文生成;研究设计仅在质性研究与问卷量化研究之间根据论文题目和关键词识别。",
280
  "drop_research_materials": "将研究资料拖到这里",
281
  "limit_research_materials": "每个文件最大 1 GB · PDF, TXT, DOC, DOCX, CSV, XLS, XLSX",
282
  "material_document_use_label": "如何使用上传的 PDF/DOC/DOCX 文件?",
283
  "material_use_reference": "作为参考文献资料",
284
  "material_use_research": "作为研究资料",
285
  "detected_method_label": "研究设计(自动识别,可调整)",
286
+ "method_detection_explanation": "系统根据论文题目和关键词在质性研究与问卷量化研究之间自动识别;未识别时默认质性研究,可手动调整。",
287
  "method_confirm_label": "调整研究设计",
288
  "method_confirm_placeholder": "请选择研究设计",
289
  "method_confirmation_required": "研究设计状态异常,请重新选择研究设计。",
290
  "method_resolution_invalid": "研究设计状态异常,请重新选择研究设计。",
291
+ "method_supported": "当前研究设计为“{method}”。系统将按对应研究流程生成论文。",
292
+ "specialized_method_redirect": "您的论文可能涉及面板数据 / 元分析 / fsQCA / 文献计量,我的专长是质性研究和问卷量化研究,并非这些特定方法的精专工具。研究设计的灵魂在于问题意识和逻辑链条,这是我擅长的,为您搭建一套质性研究论文框架。",
293
  "method_undetermined": "质性研究(默认)",
294
  "method_survey_path": "问卷量化研究",
295
  "method_qualitative": "质性研究",
 
 
 
 
296
  "adaptive_template_expander": "💡 推荐模板(可选)",
297
  "download_research_template": "📥 下载研究资料模板",
298
  "template_help_survey_path": "推荐使用变量或统计结果表。若未上传数据,系统会按项目规则生成用于初稿的辅助证据。",
299
  "template_help_qualitative": "推荐使用访谈、田野记录或案例材料。若未上传资料,系统会构建匿名的初稿证据包。",
 
 
 
 
300
  "template_help_undetermined": "确认研究设计后,系统将显示对应的研究资料模板。",
301
  "start_generation_unified": "🚀 开始生成",
302
  "api_key_missing": "❌ 缺少 DEEPSEEK_API_KEY,请配置API密钥。",
 
510
  "download_asq_template": "📥 Download TXT Interview Template",
511
  "start_asq": "🚀 Start ASQ Generation",
512
  "research_materials_label": "Research Materials (Optional)",
513
+ "research_materials_help": "Supports PDF, TXT, DOC, DOCX, CSV, XLS, and XLSX. Uploaded files support paper generation; Research Design is identified from the Title and Keywords between Qualitative Research and Survey-Based Quantitative Research only.",
514
  "drop_research_materials": "Drag research materials here",
515
  "limit_research_materials": "Up to 1 GB per file · PDF, TXT, DOC, DOCX, CSV, XLS, XLSX",
516
  "material_document_use_label": "How should SmartPaper use the uploaded PDF/DOC/DOCX files?",
517
  "material_use_reference": "Use as reference literature",
518
  "material_use_research": "Use as research materials",
519
  "detected_method_label": "Research Design (Auto-detected, Adjustable)",
520
+ "method_detection_explanation": "SmartPaper identifies the Research Design from the Title and Keywords using two supported options: Qualitative Research and Survey-Based Quantitative Research. If neither is clearly indicated, Qualitative Research is used by default. You can adjust the selection manually.",
521
  "method_confirm_label": "Adjust Research Design",
522
  "method_confirm_placeholder": "Select a Research Design",
523
  "method_confirmation_required": "The Research Design state is invalid. Please select a Research Design again.",
524
  "method_resolution_invalid": "The Research Design state is invalid. Please select a Research Design again.",
525
+ "method_supported": "Current Research Design: {method}. SmartPaper will use the corresponding research workflow.",
526
+ "specialized_method_redirect": "Your paper may involve panel data, meta-analysis, fsQCA, or bibliometric methods. My core strengths are qualitative research and survey-based quantitative research; I am not designed as a specialist tool for those specific methods. The essence of research design lies in a clear research problem and a coherent chain of reasoning—areas in which I am strongest. I will therefore help you build a qualitative research paper framework.",
527
  "method_undetermined": "Qualitative Research (Default)",
528
  "method_survey_path": "Survey-Based Quantitative Research",
529
  "method_qualitative": "Qualitative Research",
 
 
 
 
530
  "adaptive_template_expander": "💡 Recommended Template (Optional)",
531
  "download_research_template": "📥 Download Research Materials Template",
532
  "template_help_survey_path": "A variable-level or statistical-results table is recommended. If no data are uploaded, SmartPaper may construct draft evidence under the project provenance rules.",
533
  "template_help_qualitative": "Interview, field-note, or case materials are recommended. If none are uploaded, SmartPaper may construct an anonymous draft evidence packet.",
 
 
 
 
534
  "template_help_undetermined": "Confirm the research design to view the corresponding research-material template.",
535
  "start_generation_unified": "🚀 Start Generation",
536
  "api_key_missing": "❌ DEEPSEEK_API_KEY is missing. Configure the API key.",
 
744
  "download_asq_template": "📥 Скачать TXT-шаблон интервью",
745
  "start_asq": "🚀 Запустить формирование в режиме ASQ",
746
  "research_materials_label": "Материалы исследования (необязательно)",
747
+ "research_materials_help": "Поддерживаются PDF, TXT, DOC, DOCX, CSV, XLS и XLSX. Загруженные файлы используются как дополнительные исследовательские или справ��чные материалы; дизайн исследования определяется по названию статьи и ключевым словам только между качественным исследованием и количественным исследованием на основе опроса.",
748
  "drop_research_materials": "Перетащите исследовательские материалы сюда",
749
  "limit_research_materials": "До 1 ГБ на файл · PDF, TXT, DOC, DOCX, CSV, XLS, XLSX",
750
  "material_document_use_label": "Как использовать загруженные файлы PDF/DOC/DOCX?",
751
  "material_use_reference": "Как научные источники",
752
  "material_use_research": "Как исследовательские материалы",
753
  "detected_method_label": "Дизайн исследования (определяется автоматически, можно изменить)",
754
+ "method_detection_explanation": "SmartPaper определяет дизайн исследования по названию статьи и ключевым словам, выбирая между двумя поддерживаемыми вариантами: качественным исследованием и количественным исследованием на основе опроса. Если явных признаков нет, по умолчанию выбирается качественное исследование. Выбор можно изменить вручную.",
755
  "method_confirm_label": "Изменить дизайн исследования",
756
  "method_confirm_placeholder": "Выберите дизайн исследования",
757
  "method_confirmation_required": "Некорректное состояние дизайна исследования. Выберите дизайн исследования повторно.",
758
  "method_resolution_invalid": "Некорректное состояние дизайна исследования. Выберите дизайн исследования повторно.",
759
+ "method_supported": "Текущий дизайн исследования: «{method}». SmartPaper применит соответствующий исследовательский процесс.",
760
+ "specialized_method_redirect": "Ваша статья, возможно, предполагает использование панельных данных, метаанализа, fsQCA или библиометрических методов. Моя основная специализация — качественные исследования и количественные исследования на основе опросов; я не предназначен как узкоспециализированный инструмент для этих конкретных методов. Суть исследовательского дизайна заключается в чёткой постановке исследовательской проблемы и последовательной логике аргументации — именно в этом моя сильная сторона. Поэтому я помогу вам выстроить структуру статьи в рамках качественного исследования.",
761
  "method_undetermined": "Качественное исследование (по умолчанию)",
762
  "method_survey_path": "Количественное исследование на основе опроса",
763
  "method_qualitative": "Качественное исследование",
 
 
 
 
764
  "adaptive_template_expander": "💡 Рекомендуемый шаблон (необязательно)",
765
  "download_research_template": "📥 Скачать шаблон исследовательских материалов",
766
  "template_help_survey_path": "Рекомендуется таблица переменных или статистических результатов. При отсутствии данных SmartPaper может сформировать черновые доказательные материалы в рамках правил происхождения данных.",
767
  "template_help_qualitative": "Рекомендуются интервью, полевые заметки или материалы кейсов. При их отсутствии SmartPaper может сформировать анонимный черновой пакет доказательств.",
 
 
 
 
768
  "template_help_undetermined": "После подтверждения дизайна исследования будет показан соответствующий шаблон материалов.",
769
  "start_generation_unified": "🚀 Начать",
770
  "api_key_missing": "❌ Не задан DEEPSEEK_API_KEY. Настройте ключ API.",
 
978
  "download_asq_template": "📥 TXT сұхбат үлгісін жүктеу",
979
  "start_asq": "🚀 ASQ режимінде дайындауды бастау",
980
  "research_materials_label": "Зерттеу материалдары (міндетті емес)",
981
+ "research_materials_help": "PDF, TXT, DOC, DOCX, CSV, XLS және XLSX форматтары қолдау табады. Жүктелген файлдар қосымша зерттеу немесе анықтамалық материал ретінде пайдаланылады; зерттеу дизайны мақала тақырыбы мен кілт сөздер негізінде тек сапалық зерттеу мен сауалнамаға негізделген сандық зерттеу арасынан анықталады.",
982
  "drop_research_materials": "Зерттеу материалдарын осы жерге сүйреп әкеліңіз",
983
  "limit_research_materials": "Әр файлға 1 ГБ дейін · PDF, TXT, DOC, DOCX, CSV, XLS, XLSX",
984
  "material_document_use_label": "Жүктелген PDF/DOC/DOCX файлдарын қалай пайдалану керек?",
985
  "material_use_reference": "Ғылыми дереккөз ретінде",
986
  "material_use_research": "Зерттеу материалы ретінде",
987
  "detected_method_label": "Зерттеу дизайны (автоматты түрде анықталады, өзгертуге болады)",
988
+ "method_detection_explanation": "SmartPaper мақала тақырыбы мен кілт сөздер негізінде екі қолдау көрсетілетін н��сқаның бірін анықтайды: сапалық зерттеу немесе сауалнамаға негізделген сандық зерттеу. Нақты белгі болмаса, әдепкі бойынша сапалық зерттеу таңдалады. Таңдауды қолмен өзгертуге болады.",
989
  "method_confirm_label": "Зерттеу дизайнын өзгерту",
990
  "method_confirm_placeholder": "Зерттеу дизайнын таңдаңыз",
991
  "method_confirmation_required": "Зерттеу дизайнының күйі жарамсыз. Зерттеу дизайнын қайта таңдаңыз.",
992
  "method_resolution_invalid": "Зерттеу дизайнының күйі жарамсыз. Зерттеу дизайнын қайта таңдаңыз.",
993
+ "method_supported": "Ағымдағы зерттеу дизайны: «{method}». SmartPaper тиісті зерттеу үдерісін қолданады.",
994
+ "specialized_method_redirect": "Сіздің мақалаңыз панельдік деректерді, мета-талдауды, fsQCA-ны немесе библиометриялық әдістерді қамтуы мүмкін. Менің негізгі мамандануым — сапалық зерттеу және сауалнамаға негізделген сандық зерттеу; бұл нақты әдістерге арналған тар бейінді құрал ретінде жасалмағанмын. Зерттеу дизайнының өзегі — зерттеу мәселесін дәл қою және логикалық тізбекті дәйекті құру, бұл — менің мықты тұсым. Сондықтан сізге сапалық зерттеу мақаласының тұтас құрылымын құруға көмектесемін.",
995
  "method_undetermined": "Сапалық зерттеу (әдепкі)",
996
  "method_survey_path": "Сауалнамаға негізделген сандық зерттеу",
997
  "method_qualitative": "Сапалық зерттеу",
 
 
 
 
998
  "adaptive_template_expander": "💡 Ұсынылатын үлгі (міндетті емес)",
999
  "download_research_template": "📥 Зерттеу материалдары үлгісін жүктеу",
1000
  "template_help_survey_path": "Айнымалылар немесе статистикалық нәтижелер кестесі ұсынылады. Дерек жүктелмесе, SmartPaper жоба ережелеріне сәйкес бастапқы нұсқаға арналған көмекші дәлелдерді құра алады.",
1001
  "template_help_qualitative": "Сұхбат, далалық жазба немесе кейс материалдары ұсынылады. Олар болмаса, SmartPaper анонимді бастапқы дәлелдер пакетін құра алады.",
 
 
 
 
1002
  "template_help_undetermined": "Зерттеу дизайнын растағаннан кейін тиісті материал үлгісі көрсетіледі.",
1003
  "start_generation_unified": "🚀 Бастау",
1004
  "api_key_missing": "❌ DEEPSEEK_API_KEY көрсетілмеген. API кілтін баптаңыз.",
 
1177
 
1178
 
1179
  def get_research_material_template(language: str, method_family: str) -> dict[str, str]:
1180
+ """Return a template for one of the two supported production designs."""
1181
  family = str(method_family or "").strip().casefold()
1182
  if family == "survey_path":
1183
  return {
 
1186
  "filename": "Survey_Path_Data_Template.csv",
1187
  "mime_type": "text/csv",
1188
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1189
  return {
1190
+ "content": get_asq_txt_template(language),
1191
+ "code_language": "text",
1192
+ "filename": "Qualitative_Research_Materials_Template.txt",
1193
+ "mime_type": "text/plain",
1194
  }
1195
 
1196