Phase 2: Add reasoning and TipTap content endpoints
- Extend Document model with reasoning_type, confidence, reasoning_steps, model_source, tiptap_content fields
- Add new endpoints:
- GET /documents/{id}/reasoning - Get reasoning metadata
- PATCH /documents/{id}/reasoning - Update reasoning metadata
- GET /documents/{id}/reasoning-panel - Get reasoning panel data for UI
- POST /documents/{id}/reasoning-steps - Add reasoning step
- DELETE /documents/{id}/reasoning-steps/{step} - Delete reasoning step
- GET /documents/{id}/content?format=tiptap|markdown - Get content in TipTap or Markdown
- PUT /documents/{id}/content - Update content (supports both TipTap JSON and Markdown)
- Add TipTap to Markdown conversion
- Update database schema with new columns
- Add comprehensive tests for all new endpoints
- All 37 tests passing
This commit is contained in:
@@ -1,6 +1,15 @@
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
class ReasoningType(str, Enum):
|
||||
CHAIN = "chain"
|
||||
IDEA = "idea"
|
||||
CONTEXT = "context"
|
||||
REFLECTION = "reflection"
|
||||
|
||||
|
||||
class DocumentCreate(BaseModel):
|
||||
@@ -18,6 +27,19 @@ class DocumentContentUpdate(BaseModel):
|
||||
content: str = Field(..., max_length=1_000_000) # 1MB limit
|
||||
|
||||
|
||||
# Phase 2: TipTap Content Update - Union to support both legacy string and new TipTap/Markdown formats
|
||||
class TipTapContentUpdate(BaseModel):
|
||||
content: dict[str, Any] | str # TipTap JSON object or Markdown string
|
||||
format: str = "tiptap"
|
||||
|
||||
@field_validator("format")
|
||||
@classmethod
|
||||
def validate_format(cls, v: str) -> str:
|
||||
if v not in ("tiptap", "markdown"):
|
||||
raise ValueError("format must be 'tiptap' or 'markdown'")
|
||||
return v
|
||||
|
||||
|
||||
class TagInfo(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
@@ -26,6 +48,67 @@ class TagInfo(BaseModel):
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
# Phase 2: Reasoning Step Schema
|
||||
class ReasoningStep(BaseModel):
|
||||
step: int
|
||||
thought: str = Field(..., max_length=2000)
|
||||
conclusion: str | None = Field(None, max_length=2000)
|
||||
|
||||
|
||||
# Phase 2: Reasoning Metadata Schema
|
||||
class ReasoningMetadata(BaseModel):
|
||||
reasoning_type: ReasoningType | None = None
|
||||
confidence: float | None = None
|
||||
reasoning_steps: list[ReasoningStep] = []
|
||||
model_source: str | None = None
|
||||
|
||||
model_config = {"protected_namespaces": ()}
|
||||
|
||||
@field_validator("confidence")
|
||||
@classmethod
|
||||
def validate_confidence(cls, v: float | None) -> float | None:
|
||||
if v is not None and (v < 0.0 or v > 1.0):
|
||||
raise ValueError("confidence must be between 0.0 and 1.0")
|
||||
return v
|
||||
|
||||
|
||||
# Phase 2: Reasoning Update Schema
|
||||
class ReasoningUpdate(BaseModel):
|
||||
reasoning_type: ReasoningType | None = None
|
||||
confidence: float | None = None
|
||||
reasoning_steps: list[ReasoningStep] | None = None
|
||||
model_source: str | None = None
|
||||
|
||||
model_config = {"protected_namespaces": ()}
|
||||
|
||||
@field_validator("confidence")
|
||||
@classmethod
|
||||
def validate_confidence(cls, v: float | None) -> float | None:
|
||||
if v is not None and (v < 0.0 or v > 1.0):
|
||||
raise ValueError("confidence must be between 0.0 and 1.0")
|
||||
return v
|
||||
|
||||
|
||||
# Phase 2: Add Reasoning Step Schema
|
||||
class ReasoningStepAdd(BaseModel):
|
||||
thought: str = Field(..., min_length=1, max_length=2000)
|
||||
conclusion: str | None = Field(None, max_length=2000)
|
||||
|
||||
|
||||
# Phase 2: TipTap Content Response
|
||||
class TipTapContentResponse(BaseModel):
|
||||
content: dict[str, Any] | str
|
||||
format: str
|
||||
|
||||
|
||||
# Phase 2: Reasoning Panel Schema
|
||||
class ReasoningPanel(BaseModel):
|
||||
document_id: str
|
||||
has_reasoning: bool
|
||||
reasoning: ReasoningMetadata | None = None
|
||||
editable: bool = True
|
||||
|
||||
|
||||
class DocumentResponse(BaseModel):
|
||||
id: str
|
||||
title: str
|
||||
@@ -36,8 +119,14 @@ class DocumentResponse(BaseModel):
|
||||
tags: list[TagInfo] = []
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
# Phase 2: new fields
|
||||
reasoning_type: ReasoningType | None = None
|
||||
confidence: float | None = None
|
||||
reasoning_steps: list[ReasoningStep] = []
|
||||
model_source: str | None = None
|
||||
tiptap_content: dict[str, Any] | None = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
model_config = {"from_attributes": True, "protected_namespaces": ()}
|
||||
|
||||
|
||||
class DocumentBriefResponse(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user