Merge origin/development into main; resolve conflicts using development.
This commit is contained in:
@@ -35,6 +35,7 @@ from app.agents.stage_constants import (
|
||||
STAGE_TO_ORDER,
|
||||
)
|
||||
from app.agents.state_schema import MemoirStateSchema
|
||||
from app.core.business_telemetry import business_span
|
||||
from app.core.config import settings
|
||||
from app.core.logging import get_logger
|
||||
from app.features.conversation.lineage_schemas import aggregate_lineage_from_segments
|
||||
@@ -61,6 +62,8 @@ from app.features.memoir.repo import (
|
||||
)
|
||||
from app.features.memory.evidence_format import format_evidence_chunks_for_prompt
|
||||
from app.features.story.models import Story, StoryVersion
|
||||
from app.features.memoir.constants import memoir
|
||||
from app.features.story.constants import story
|
||||
from app.features.story.sync_write import (
|
||||
append_story_version_sync,
|
||||
count_story_versions_sync,
|
||||
@@ -267,7 +270,7 @@ def _title_slots_filtered_for_generation(
|
||||
slot_snippets: dict[str, str], *, md: str, oral_scope: str
|
||||
) -> dict[str, str]:
|
||||
"""仅保留与正文或本批口述有文本重叠的 slot,降低档案/历史 slot 串台到标题。"""
|
||||
if not settings.memoir_title_slots_require_body_or_oral_match:
|
||||
if not memoir.title_slots_require_body_or_oral_match:
|
||||
return dict(slot_snippets)
|
||||
hay = f"{(md or '').strip()}\n{(oral_scope or '').strip()}"
|
||||
if not hay.strip():
|
||||
@@ -311,7 +314,7 @@ def _strip_ungrounded_title_segments(
|
||||
"""
|
||||
按 · / • 分节丢弃含未落地履历短语的小节;全部丢弃则占位。
|
||||
"""
|
||||
if not settings.memoir_title_hay_grounding_strict_phrases_enabled:
|
||||
if not memoir.title_hay_grounding_strict_phrases_enabled:
|
||||
return (title or "").strip() or _placeholder_title(
|
||||
chapter_category, language=language
|
||||
)
|
||||
@@ -358,7 +361,7 @@ def _maybe_generate_title(
|
||||
) -> str:
|
||||
"""Generate a title only when body is long enough; otherwise return placeholder."""
|
||||
body_len = len((md or "").strip())
|
||||
if body_len < settings.story_title_min_body_chars:
|
||||
if body_len < story.title_min_body_chars:
|
||||
return _placeholder_title(chapter_category, language=language)
|
||||
content_excerpt = (md or "").strip()[:300]
|
||||
merged_slots = _title_slots_filtered_for_generation(
|
||||
@@ -392,8 +395,8 @@ def _route_segment_texts(category_segments: list) -> list[tuple[str, str]]:
|
||||
for seg in category_segments:
|
||||
raw = seg.user_input_text or ""
|
||||
if (
|
||||
settings.memoir_oral_normalize_enabled
|
||||
and (settings.memoir_oral_normalize_mode or "rules").strip().lower()
|
||||
memoir.oral_normalize_enabled
|
||||
and (memoir.oral_normalize_mode or "rules").strip().lower()
|
||||
!= "off"
|
||||
):
|
||||
t = apply_oral_rules(raw)
|
||||
@@ -435,7 +438,7 @@ def _gate_narrative_fidelity(
|
||||
from app.agents.memoir.fidelity_check_agent import FidelityCheckAgent
|
||||
|
||||
check_llm = fidelity_llm if fidelity_llm is not None else llm
|
||||
if not settings.memoir_fidelity_check_enabled or not check_llm:
|
||||
if not memoir.fidelity_check_enabled or not check_llm:
|
||||
return narrative_raw, "none"
|
||||
agent = FidelityCheckAgent()
|
||||
ex = (existing_canonical or "").strip() or None
|
||||
@@ -471,7 +474,7 @@ def _apply_narrative_body_safety(
|
||||
m = (md or "").strip()
|
||||
ex = (existing_for_narrative or "").strip()
|
||||
o = (oral or "").strip()
|
||||
min_len = int(settings.memoir_narrative_evidence_overlap_min_chars)
|
||||
min_len = int(memoir.narrative_evidence_overlap_min_chars)
|
||||
ev_plain = strip_evidence_for_overlap_check(evidence_text)
|
||||
if m and body_contains_prompt_artifact(m):
|
||||
logger.warning(
|
||||
@@ -494,7 +497,7 @@ def _apply_narrative_body_safety(
|
||||
"evidence_leak_heuristic"
|
||||
)
|
||||
if (
|
||||
settings.memoir_evidence_scene_anchor_check_enabled
|
||||
memoir.evidence_scene_anchor_check_enabled
|
||||
and m
|
||||
and evidence_text.strip()
|
||||
and evidence_scene_anchor_leak(m, ev_plain, o, ex)
|
||||
@@ -667,8 +670,8 @@ def _resolve_append_target(
|
||||
memoir_correlation_id: str | None,
|
||||
) -> tuple[str | None, str, str]:
|
||||
"""Resolve append target and return (target_story_id, existing_for_narrative, decision_source)."""
|
||||
max_chars = int(settings.story_append_max_canonical_chars)
|
||||
max_ver = int(settings.story_append_max_versions)
|
||||
max_chars = int(story.append_max_canonical_chars)
|
||||
max_ver = int(story.append_max_versions)
|
||||
target_story_id: str | None = None
|
||||
existing_for_narrative = ""
|
||||
|
||||
@@ -696,7 +699,7 @@ def _resolve_append_target(
|
||||
and candidate_stories
|
||||
and decision_source not in FALLBACK_NEW_STORY_REASONS
|
||||
and len(oral_norm)
|
||||
<= int(settings.memoir_story_route_append_guardrail_oral_chars)
|
||||
<= int(memoir.story_route_append_guardrail_oral_chars)
|
||||
):
|
||||
tid_g = default_append_target_story_id(candidate_stories, story_meta, settings)
|
||||
if tid_g:
|
||||
@@ -817,7 +820,13 @@ def _execute_narrative_unit(
|
||||
|
||||
if target_story_id:
|
||||
sid_s = str(target_story_id)
|
||||
ver = append_story_version_sync(session, sid_s, md)
|
||||
try:
|
||||
ver = append_story_version_sync(session, sid_s, md)
|
||||
except ValueError as exc:
|
||||
logger.warning(
|
||||
"append_story_version_sync failed story_id={}: {}", sid_s, exc
|
||||
)
|
||||
return None
|
||||
_persist_story_lineage_sync(
|
||||
session,
|
||||
story_id=sid_s,
|
||||
@@ -1049,9 +1058,9 @@ def _run_story_pipeline_batch_inner(
|
||||
source_ids = [seg.id for seg in category_segments]
|
||||
|
||||
n_units = len(category_segments)
|
||||
top_k = int(settings.evidence_top_k_default)
|
||||
if n_units > int(settings.evidence_large_batch_threshold):
|
||||
top_k = int(settings.evidence_top_k_large_batch)
|
||||
top_k = int(story.evidence_top_k_default)
|
||||
if n_units > int(story.evidence_large_batch_threshold):
|
||||
top_k = int(story.evidence_top_k_large_batch)
|
||||
|
||||
def _oral_job() -> tuple[str, float]:
|
||||
with business_span("memoir.story_pipeline.oral_normalize"):
|
||||
@@ -1178,7 +1187,7 @@ def _run_story_pipeline_batch_inner(
|
||||
plan is None
|
||||
and single_route is not None
|
||||
and single_route.reason in FALLBACK_NEW_STORY_REASONS
|
||||
and bool(settings.memoir_route_defer_enabled)
|
||||
and bool(memoir.route_defer_enabled)
|
||||
):
|
||||
defer_ids = [str(s.id) for s in category_segments]
|
||||
logger.info(
|
||||
|
||||
Reference in New Issue
Block a user