refactor(api): TOML 配置 SSOT、统一错误契约、Auth/事务加固与可观测性 (#33)
配置 SSOT(TOML + .env) 统一错误契约 Auth 与事务边界 Redis / Celery 可靠性:业务 Redis(DB/0)与 Celery broker/backend(DB/1)显式拆分;连接池、sync client 可观测性(OpenTelemetry + LGTM)
This commit is contained in:
@@ -24,7 +24,9 @@ from app.agents.state_schema import (
|
||||
narrative_coverage_state,
|
||||
)
|
||||
from app.core.config import settings
|
||||
from app.core.db import transactional, transactional_sync
|
||||
from app.features.memoir.models import MemoirState as MemoirStateModel
|
||||
from app.features.memoir.constants import memoir
|
||||
|
||||
|
||||
def _slots_snapshot_for_merge(raw: Dict[str, Dict] | None) -> Dict[str, Dict]:
|
||||
@@ -83,8 +85,8 @@ async def get_or_create_state(user_id: str, db: AsyncSession) -> MemoirStateSche
|
||||
for k, v in default.slots.items()
|
||||
},
|
||||
)
|
||||
db.add(state)
|
||||
await db.commit()
|
||||
async with transactional(db):
|
||||
db.add(state)
|
||||
await db.refresh(state)
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
@@ -101,7 +103,7 @@ def _apply_current_stage_policy(
|
||||
state.current_stage = stage_norm
|
||||
return
|
||||
|
||||
if not settings.memoir_extraction_updates_current_stage:
|
||||
if not memoir.extraction_updates_current_stage:
|
||||
return
|
||||
cur_b = chat_bucket(state.current_stage or current_from_db)
|
||||
new_b = chat_bucket(stage_norm)
|
||||
@@ -140,20 +142,20 @@ async def update_slot(
|
||||
if slot_name not in allowed_slot_names_for_stage(stage_norm, current_from_db):
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
slots = _slots_snapshot_for_merge(
|
||||
state.slots if isinstance(state.slots, dict) else None
|
||||
)
|
||||
stage_slots = dict(slots.get(stage_norm, {}) or {})
|
||||
existing = stage_slots.get(slot_name, {})
|
||||
async with transactional(db):
|
||||
slots = _slots_snapshot_for_merge(
|
||||
state.slots if isinstance(state.slots, dict) else None
|
||||
)
|
||||
stage_slots = dict(slots.get(stage_norm, {}) or {})
|
||||
existing = stage_slots.get(slot_name, {})
|
||||
|
||||
merged_segment_ids = list({*(existing.get("segment_ids") or []), *segment_ids})
|
||||
stage_slots[slot_name] = SlotData(
|
||||
snippet=snippet, segment_ids=merged_segment_ids
|
||||
).model_dump()
|
||||
slots[stage_norm] = stage_slots
|
||||
state.slots = slots
|
||||
_apply_current_stage_policy(state, stage_norm, memoir_batch=memoir_batch)
|
||||
await db.commit()
|
||||
merged_segment_ids = list({*(existing.get("segment_ids") or []), *segment_ids})
|
||||
stage_slots[slot_name] = SlotData(
|
||||
snippet=snippet, segment_ids=merged_segment_ids
|
||||
).model_dump()
|
||||
slots[stage_norm] = stage_slots
|
||||
state.slots = slots
|
||||
_apply_current_stage_policy(state, stage_norm, memoir_batch=memoir_batch)
|
||||
await db.refresh(state)
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
@@ -168,19 +170,19 @@ async def mark_stage_complete(
|
||||
if not state:
|
||||
return await get_or_create_state(user_id, db)
|
||||
|
||||
covered = state.covered_stages or []
|
||||
if stage not in covered:
|
||||
covered.append(stage)
|
||||
state.covered_stages = covered
|
||||
async with transactional(db):
|
||||
covered = state.covered_stages or []
|
||||
if stage not in covered:
|
||||
covered.append(stage)
|
||||
state.covered_stages = covered
|
||||
|
||||
stage_order = state.stage_order or default_state().stage_order
|
||||
if state.current_stage == stage:
|
||||
try:
|
||||
idx = stage_order.index(stage)
|
||||
state.current_stage = stage_order[min(idx + 1, len(stage_order) - 1)]
|
||||
except ValueError:
|
||||
state.current_stage = default_state().current_stage
|
||||
await db.commit()
|
||||
stage_order = state.stage_order or default_state().stage_order
|
||||
if state.current_stage == stage:
|
||||
try:
|
||||
idx = stage_order.index(stage)
|
||||
state.current_stage = stage_order[min(idx + 1, len(stage_order) - 1)]
|
||||
except ValueError:
|
||||
state.current_stage = default_state().current_stage
|
||||
await db.refresh(state)
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
@@ -205,11 +207,11 @@ async def switch_stage(
|
||||
result = await db.execute(stmt)
|
||||
state = result.scalar_one()
|
||||
|
||||
fb = state.current_stage or "childhood"
|
||||
state.current_stage = normalize_chat_stage(
|
||||
new_stage, fallback=fb, log_context={"user_id": user_id}
|
||||
)
|
||||
await db.commit()
|
||||
async with transactional(db):
|
||||
fb = state.current_stage or "childhood"
|
||||
state.current_stage = normalize_chat_stage(
|
||||
new_stage, fallback=fb, log_context={"user_id": user_id}
|
||||
)
|
||||
await db.refresh(state)
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
@@ -234,10 +236,10 @@ async def save_interview_state_meta(
|
||||
result = await db.execute(stmt)
|
||||
state = result.scalar_one()
|
||||
|
||||
state.known_facts_json = [x.model_dump() for x in known_facts]
|
||||
state.persona_threads_json = [x.model_dump() for x in persona_threads]
|
||||
state.recent_questions_json = list(recent_questions)
|
||||
await db.commit()
|
||||
async with transactional(db):
|
||||
state.known_facts_json = [x.model_dump() for x in known_facts]
|
||||
state.persona_threads_json = [x.model_dump() for x in persona_threads]
|
||||
state.recent_questions_json = list(recent_questions)
|
||||
await db.refresh(state)
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
@@ -261,8 +263,8 @@ def get_or_create_state_sync(user_id: str, db: Session) -> MemoirStateSchema:
|
||||
for k, v in default.slots.items()
|
||||
},
|
||||
)
|
||||
db.add(state)
|
||||
db.commit()
|
||||
with transactional_sync(db):
|
||||
db.add(state)
|
||||
db.refresh(state)
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
@@ -298,19 +300,19 @@ def update_slot_sync(
|
||||
if slot_name not in allowed_slot_names_for_stage(stage_norm, current_from_db):
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
slots = _slots_snapshot_for_merge(
|
||||
state.slots if isinstance(state.slots, dict) else None
|
||||
)
|
||||
stage_slots = dict(slots.get(stage_norm, {}) or {})
|
||||
existing = stage_slots.get(slot_name, {})
|
||||
with transactional_sync(db):
|
||||
slots = _slots_snapshot_for_merge(
|
||||
state.slots if isinstance(state.slots, dict) else None
|
||||
)
|
||||
stage_slots = dict(slots.get(stage_norm, {}) or {})
|
||||
existing = stage_slots.get(slot_name, {})
|
||||
|
||||
merged_segment_ids = list({*(existing.get("segment_ids") or []), *segment_ids})
|
||||
stage_slots[slot_name] = SlotData(
|
||||
snippet=snippet, segment_ids=merged_segment_ids
|
||||
).model_dump()
|
||||
slots[stage_norm] = stage_slots
|
||||
state.slots = slots
|
||||
_apply_current_stage_policy(state, stage_norm, memoir_batch=memoir_batch)
|
||||
db.commit()
|
||||
merged_segment_ids = list({*(existing.get("segment_ids") or []), *segment_ids})
|
||||
stage_slots[slot_name] = SlotData(
|
||||
snippet=snippet, segment_ids=merged_segment_ids
|
||||
).model_dump()
|
||||
slots[stage_norm] = stage_slots
|
||||
state.slots = slots
|
||||
_apply_current_stage_policy(state, stage_norm, memoir_batch=memoir_batch)
|
||||
db.refresh(state)
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
Reference in New Issue
Block a user