feat: add smart page creation endpoint with HybridRAG research

Add POST /wiki/pages/smart-create endpoint that combines research with
content generation for the librarian agent:

- Run HybridRAG search on topic (wiki + graph + web)
- Use LLM (WikiPageWriter) to synthesize findings into wiki content
- Create page with proper attribution and sources
- Schedule background tasks for vector/graph indexing
- Apply bidirectional entity linking (forward + backward links)

New files:
- src/services/entity_linking_utils.py - shared entity linking helper

Modified:
- src/models/wiki.py - WikiSmartCreateRequest/Response models
- src/services/wiki_service.py - smart_create_page() method
- src/routers/wiki.py - /pages/smart-create endpoint

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-11 20:23:46 +01:00
co-authored by Claude Opus 4.5
parent 95852190ba
commit e05e7aeae3
4 changed files with 492 additions and 3 deletions
+42 -1
View File
@@ -8,7 +8,7 @@ Models for:
"""
from pydantic import BaseModel, Field, field_validator
from typing import Optional, List
from typing import Optional, List, Dict, Any
from datetime import datetime
@@ -190,3 +190,44 @@ class DossierOperationResponse(BaseModel):
dossier_name: str = Field(..., description="Dossier name")
index_page_id: Optional[int] = Field(None, description="Index page ID (if created)")
index_page_path: Optional[str] = Field(None, description="Index page path (if created)")
# Smart create models (HybridRAG-powered page creation)
class WikiSmartCreateRequest(BaseModel):
"""Request model for smart page creation with research."""
topic: str = Field(..., min_length=1, max_length=500, description="Topic to research and create page about")
path: Optional[str] = Field(None, description="Page path (auto-generated from topic if not provided)")
tags: List[str] = Field(default_factory=list, description="Tags for the page")
user: Optional[str] = Field(None, description="User identifier")
include_web_research: bool = Field(default=True, description="Include web search results")
include_wiki_search: bool = Field(default=True, description="Include existing wiki knowledge")
@field_validator("tags")
@classmethod
def validate_tags(cls, v: List[str]) -> List[str]:
"""Validate and clean tags."""
cleaned = [tag.strip() for tag in v if tag.strip()]
return list(set(cleaned))
@field_validator("path")
@classmethod
def validate_path(cls, v: Optional[str]) -> Optional[str]:
"""Validate page path if provided."""
if v is None:
return None
# Ensure path starts with /
if not v.startswith("/"):
v = f"/{v}"
# Remove trailing slash
if v.endswith("/") and v != "/":
v = v.rstrip("/")
return v
class WikiSmartCreateResponse(BaseModel):
"""Response model for smart page creation."""
page: WikiPage = Field(..., description="Created wiki page")
research_summary: Dict[str, Any] = Field(..., description="Summary of research used")
sources_used: int = Field(..., description="Number of sources incorporated")
search_id: Optional[str] = Field(None, description="HybridRAG search ID for reference")
entity_linking: Dict[str, int] = Field(default_factory=dict, description="Entity linking statistics")