8a8096a444
POST /documents/{id}/analyze 호출을 DB에 기록. failure mode 분류 + source 식별.
- migrations/137: analyze_events 테이블 (doc_id FK, mode, truncated, layers_returned JSONB, cached, latency_ms, error_code, source TEXT NOT NULL DEFAULT 'document_server', prompt_version)
- ORM: models/analyze_event.py 신규
- services/document_telemetry.py: record_analyze_event() + sanitize_source() 서버 fallback 강제 (enum 외 → unknown, None → document_server)
- app/api/documents.py:
· X-Source 헤더 + BackgroundTasks 의존성 추가
· try/finally 패턴으로 성공/cache/에러 모든 exit에서 background insert
· error_code: None(성공) | not_found | no_text | timeout | llm | parse | missing_summary
Phase F에서 nanoclaude가 X-Source: synology_chat 헤더로 호출하면 source 구분 가능.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
43 lines
1.9 KiB
Python
43 lines
1.9 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 BigInteger, Boolean, DateTime, ForeignKey, Integer, Text
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from core.database import Base
|
|
|
|
|
|
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
|
|
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
|
|
)
|