235bbf9881
사용자 '공평하게 동일한 작업' 지적의 비대칭 잔재 2건 + 예고된 배칭 레버: - queue_drain --stage classify (use_deep: deep 슬롯 endpoint + triage sampling, 완료 시 enqueue_next_stage 로 embed/chunk/markdown 연쇄 — DAG 단절 방지) - deep_summary consumer = 맥북 우선, 불가 시 맥미니 primary 즉시 처리(동일 모델 — 강등 아님). drain 은 defer_on_deep_unavailable=True 로 기존 보류-종료 유지 - llm_gate capacity 일반화 (config pipeline.mlx_gate_concurrency, 기본 1, 운영 2) — 'MLX_CONCURRENCY=1 고정' 영구 룰의 전제(single-inference 서버) 소멸을 docstring 에 개정 박제 - analyze_events FK(users) CLI 컨텍스트 INSERT 실패 fix (models.user 명시 import) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
69 lines
3.7 KiB
Python
69 lines
3.7 KiB
Python
"""analyze_events 테이블 ORM — POST /documents/{id}/analyze 호출 관측 (Phase E.2)
|
|
|
|
목적: 분석 failure mode 분류 (timeout / parse / llm / missing_summary) +
|
|
source 별 사용 패턴 (document_server / synology_chat / ui_search / ui_detail / eval).
|
|
단계 3 snapshot DB 설계 입력이 됨.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from sqlalchemy import ARRAY, BigInteger, Boolean, DateTime, Float, ForeignKey, Integer, Text
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from core.database import Base
|
|
|
|
# FK("users.id") 해석에 users 테이블 메타데이터 필요 — fastapi 앱은 어차피 전 모델을
|
|
# import 하지만, CLI 단독 실행(queue_drain 등)은 본 모듈만 끌어와 INSERT 시
|
|
# "could not find table 'users'" 로 실패했다 (2026-06-12 drain 로그 실측). 명시 import.
|
|
from models.user import User # noqa: F401
|
|
|
|
|
|
class AnalyzeEvent(Base):
|
|
__tablename__ = "analyze_events"
|
|
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
doc_id: Mapped[int] = mapped_column(
|
|
BigInteger, ForeignKey("documents.id", ondelete="CASCADE"), nullable=False
|
|
)
|
|
user_id: Mapped[int | None] = mapped_column(
|
|
BigInteger, ForeignKey("users.id", ondelete="SET NULL")
|
|
)
|
|
mode: Mapped[str] = mapped_column(Text, default="quick", nullable=False) # quick / full / summary_triage / summary_deep / retrieval_select / synthesis
|
|
text_limit: Mapped[int | None] = mapped_column(Integer)
|
|
truncated: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
layers_returned: Mapped[list[Any] | None] = mapped_column(JSONB, default=list)
|
|
cached: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
latency_ms: Mapped[int | None] = mapped_column(Integer)
|
|
model_name: Mapped[str | None] = mapped_column(Text)
|
|
prompt_version: Mapped[str | None] = mapped_column(Text)
|
|
# None (success) | "timeout" | "llm" | "parse" | "missing_summary" | "no_text"
|
|
error_code: Mapped[str | None] = mapped_column(Text)
|
|
# document_server / synology_chat / ui_search / ui_detail / eval / unknown
|
|
source: Mapped[str] = mapped_column(Text, default="document_server", nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=datetime.now, nullable=False
|
|
)
|
|
|
|
# PR-A (migration 153) — routing shadow observability
|
|
subject_domain: Mapped[str | None] = mapped_column(Text)
|
|
risk_flags: Mapped[list[str] | None] = mapped_column(ARRAY(Text))
|
|
high_impact_task: Mapped[bool | None] = mapped_column(Boolean)
|
|
escalated_to_26b: Mapped[bool | None] = mapped_column(Boolean)
|
|
escalation_reasons: Mapped[list[str] | None] = mapped_column(ARRAY(Text))
|
|
confidence: Mapped[float | None] = mapped_column(Float)
|
|
policy_violation: Mapped[bool | None] = mapped_column(Boolean)
|
|
policy_violation_ids: Mapped[list[str] | None] = mapped_column(ARRAY(Text))
|
|
shadow_would_route_to: Mapped[str | None] = mapped_column(Text)
|
|
policy_version: Mapped[str | None] = mapped_column(Text)
|
|
|
|
# PR-B (migration 159) — 실제 호출 tier 와 R2 backlog guard 이벤트
|
|
tier: Mapped[str | None] = mapped_column(Text) # 'triage' | 'primary' | 'fallback'
|
|
suppressed_reason: Mapped[str | None] = mapped_column(Text) # 'backlog_guard(ratio=0.42,pending=7)'
|
|
|
|
# PR-B B-2 (migration 161) — /ask 3-state answerability 독립 컬럼
|
|
answerability: Mapped[str | None] = mapped_column(Text) # 'direct' | 'partial' | 'insufficient'
|
|
partial_basis: Mapped[bool | None] = mapped_column(Boolean) # partial 답변이 실제 생성됐는지
|
|
suggested_query_count: Mapped[int | None] = mapped_column(Integer)
|