Files
dify/api/services/annotation_query.py
T
非法操作andautofix-ci[bot] 092526a504 refactor(api): migrate Service API annotation management (#43809)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-10-27 06:20:08 +00:00

92 lines
2.5 KiB
Python

"""Detached annotation reads shared by Console and Service API endpoints.
These queries need no application orchestration. The composition root injects
their repository implementation directly, as it does for app statistics.
"""
from dataclasses import dataclass
from datetime import datetime
from typing import Protocol
class AnnotationAppNotFoundError(Exception):
"""The requested app is unavailable in the caller's workspace."""
class AnnotationNotFoundError(Exception):
"""The requested annotation does not belong to the admitted app."""
@dataclass(frozen=True, slots=True)
class AnnotationRecord:
id: str
question: str
content: str
hit_count: int
created_at: datetime
@dataclass(frozen=True, slots=True)
class AnnotationHitHistoryRecord:
id: str
source: str
score: float
question: str
created_at: datetime
annotation_question: str
annotation_content: str
@dataclass(frozen=True, slots=True)
class AnnotationPage[RecordT]:
data: tuple[RecordT, ...]
page: int
limit: int
total: int
@property
def has_more(self) -> bool:
return self.page * self.limit < self.total
@dataclass(frozen=True, slots=True)
class AnnotationEmbeddingModel:
"""Both names are None when an enabled setting's binding was removed."""
embedding_provider_name: str | None
embedding_model_name: str | None
@dataclass(frozen=True, slots=True)
class AnnotationSettingRecord:
"""Disabled apps have no setting ID, threshold, or embedding model."""
enabled: bool
id: str | None
score_threshold: float | None
embedding_model: AnnotationEmbeddingModel | None
class AnnotationAppQuery(Protocol):
def require_app(self, *, tenant_id: str, app_id: str) -> None:
"""Validate a normal app's workspace and close the database session before returning."""
...
class AnnotationQuery(Protocol):
def count(self, *, tenant_id: str, app_id: str) -> int: ...
def get_page(
self, *, tenant_id: str, app_id: str, page: int, limit: int, keyword: str
) -> AnnotationPage[AnnotationRecord]: ...
def get_all(self, *, tenant_id: str, app_id: str) -> tuple[AnnotationRecord, ...]:
"""Read all annotations for export, retaining their original text."""
...
def get_setting(self, *, tenant_id: str, app_id: str) -> AnnotationSettingRecord: ...
def get_hit_history_page(
self, *, tenant_id: str, app_id: str, annotation_id: str, page: int, limit: int
) -> AnnotationPage[AnnotationHitHistoryRecord]: ...