feat: add pydantic models and exceptions for KB API

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
mrmamongo 2026-04-20 12:08:21 +03:00
parent 2926e702d7
commit 859494a83d
4 changed files with 202 additions and 0 deletions

1
ergpt/__init__.py Normal file
View file

@ -0,0 +1 @@
__path__ = __import__("pkgutil").extend_path(__path__, __name__)

61
ergpt/kb/__init__.py Normal file
View file

@ -0,0 +1,61 @@
from .async_client import AsyncKnowledgeBaseClient
from .client import KnowledgeBaseClient
from .exceptions import (
AuthenticationError,
ErGPTError,
NotFoundError,
PermissionDeniedError,
RateLimitError,
ServerError,
ValidationError,
)
from .models import (
ChunkPagination,
CreateKnowledgeBaseRequest,
CursorPagination,
Document,
DocumentChunk,
DocumentPagePagination,
DocumentWithContentUrl,
HTTPValidationError,
KnowledgeBase,
KnowledgeBaseAggregate,
KnowledgeBaseConfig,
PagePagination,
ScoredPayload,
SearchDocument,
SearchRequest,
SearchResponse,
UpdateKnowledgeBaseRequest,
ValidationErrorDetail,
)
__all__ = [
"KnowledgeBaseClient",
"AsyncKnowledgeBaseClient",
"ErGPTError",
"AuthenticationError",
"PermissionDeniedError",
"NotFoundError",
"ValidationError",
"ServerError",
"RateLimitError",
"KnowledgeBase",
"KnowledgeBaseAggregate",
"KnowledgeBaseConfig",
"CreateKnowledgeBaseRequest",
"UpdateKnowledgeBaseRequest",
"PagePagination",
"Document",
"DocumentWithContentUrl",
"DocumentPagePagination",
"DocumentChunk",
"CursorPagination",
"ChunkPagination",
"ScoredPayload",
"SearchDocument",
"SearchRequest",
"SearchResponse",
"ValidationErrorDetail",
"HTTPValidationError",
]

28
ergpt/kb/exceptions.py Normal file
View file

@ -0,0 +1,28 @@
class ErGPTError(Exception):
pass
class AuthenticationError(ErGPTError):
pass
class PermissionDeniedError(ErGPTError):
pass
class NotFoundError(ErGPTError):
pass
class ValidationError(ErGPTError):
def __init__(self, message: str, details: list = None):
super().__init__(message)
self.details = details or []
class ServerError(ErGPTError):
pass
class RateLimitError(ErGPTError):
pass

112
ergpt/kb/models.py Normal file
View file

@ -0,0 +1,112 @@
from datetime import datetime
from typing import Any
from pydantic import BaseModel, Field
class KnowledgeBaseConfig(BaseModel):
chunk_size: int = Field(..., description="Размер каждого чанка текста")
chunk_overlap: int = Field(..., description="Перекрытие между соседними чанками")
class CreateKnowledgeBaseRequest(BaseModel):
name: str = Field(..., description="Название базы знаний")
description: str = Field(..., description="Описание базы знаний")
config: KnowledgeBaseConfig = Field(..., description="Конфигурация разбиения на чанки")
class UpdateKnowledgeBaseRequest(BaseModel):
name: str = Field(..., description="Новое название базы знаний")
description: str = Field(..., description="Новое описание базы знаний")
class KnowledgeBase(BaseModel):
id: str = Field(..., description="Уникальный идентификатор (UUID)")
name: str = Field(..., description="Название базы знаний")
description: str = Field(..., description="Описание базы знаний")
config: dict[str, Any] = Field(..., description="Конфигурация разбиения на чанки")
created_at: datetime = Field(..., description="Дата создания")
updated_at: datetime = Field(..., description="Дата последнего обновления")
user_id: str = Field(..., description="ID владельца (UUID)")
class KnowledgeBaseAggregate(KnowledgeBase):
documents_count: int = Field(..., description="Количество документов в базе знаний")
class PagePagination(BaseModel):
result: list[KnowledgeBaseAggregate] = Field(default_factory=list)
total: int = Field(..., description="Общее количество элементов")
class Document(BaseModel):
id: str = Field(..., description="Уникальный идентификатор (UUID)")
filename: str = Field(..., description="Исходное имя файла")
bucket: str = Field(..., description="Имя бакета хранилища")
kb_id: str = Field(..., description="ID родительской базы знаний (UUID)")
content_type: str = Field(..., description="MIME-тип документа")
size: int = Field(..., description="Размер файла в байтах")
created_at: datetime = Field(..., description="Дата создания")
updated_at: datetime = Field(..., description="Дата последнего обновления")
status: str = Field(..., description="Статус обработки: PENDING, PROCESSING, COMPLETED, FAILED")
error: str | None = Field(None, description="Сообщение об ошибке, если обработка не удалась")
class DocumentWithContentUrl(Document):
content_url: str = Field(..., description="Подписанная URL для скачивания документа")
class DocumentPagePagination(BaseModel):
result: list[DocumentWithContentUrl] = Field(default_factory=list)
total: int = Field(..., description="Общее количество документов")
class DocumentChunk(BaseModel):
text: str = Field(..., description="Текстовое содержимое чанка")
document_id: str = Field(..., description="ID родительского документа (UUID)")
filename: str = Field(..., description="Исходное имя файла")
chunk_id: int = Field(..., description="Порядковый номер чанка")
class CursorPagination(BaseModel):
cursor: str | None = Field(None, description="Курсор для следующей страницы")
has_more: bool = Field(..., description="Есть ли еще результаты")
class ChunkPagination(BaseModel):
pagination: CursorPagination = Field(..., description="Метаданные пагинации")
result: list[DocumentChunk] = Field(default_factory=list, description="Список чанков")
class ScoredPayload(BaseModel):
text: str = Field(..., description="Текстовое содержимое")
score: float = Field(..., description="Оценка релевантности (0.0-1.0)")
class SearchDocument(DocumentWithContentUrl):
chunks: list[ScoredPayload] = Field(
default_factory=list, description="Найденные чанки с оценками"
)
class SearchRequest(BaseModel):
query: str = Field(..., description="Поисковый запрос")
kb_id: str = Field(..., description="ID базы знаний (UUID)")
limit: int | None = Field(None, description="Максимальное количество результатов")
score_threshold: float = Field(0.2, description="Минимальный порог релевантности (0.0-1.0)")
class SearchResponse(BaseModel):
chunks: list[SearchDocument] = Field(
default_factory=list, description="Найденные документы с чанками"
)
class ValidationErrorDetail(BaseModel):
loc: list[str] = Field(default_factory=list, description="Расположение ошибки")
msg: str = Field(..., description="Сообщение об ошибке")
type: str = Field(..., description="Тип ошибки")
class HTTPValidationError(BaseModel):
detail: list[ValidationErrorDetail] = Field(default_factory=list)