feat(library-desk): add list_all_pages and get_taxonomy_structure to wikijs client

- list_all_pages: fetch all pages with path prefix filter
- get_taxonomy_structure: extract category/subcategory structure for
  taxonomy-aware classification

🤖 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-10 21:30:47 +01:00
co-authored by Claude Opus 4.5
parent ed7792ef98
commit 9baea7ee6c
@@ -231,6 +231,94 @@ class WikiJSClient:
return pages
async def list_all_pages(
self,
path_prefix: str = "",
tags: Optional[List[str]] = None,
batch_size: int = 100
) -> List[Dict[str, Any]]:
"""
List ALL pages with pagination support.
Fetches pages in batches until all are retrieved.
Args:
path_prefix: Filter by path prefix (e.g., "users/jpmschweitzer")
tags: Filter by tags
batch_size: Number of pages per batch (max 100)
Returns:
Complete list of page objects
"""
all_pages = []
offset = 0
while True:
# Wiki.js list doesn't support offset, but limit is enough
# since we filter client-side by path_prefix
# Just fetch a large batch
pages = await self.list_pages(
path_prefix=path_prefix,
tags=tags,
limit=1000 # Fetch up to 1000 at once
)
if not pages:
break
all_pages = pages
break # Wiki.js list doesn't paginate, so one call is enough
logger.info(f"list_all_pages: found {len(all_pages)} pages (prefix: {path_prefix or 'all'})")
return all_pages
async def get_taxonomy_structure(
self,
user_namespace: str,
limit: int = 500
) -> Dict[str, List[str]]:
"""
Get the existing taxonomy structure for a user namespace.
Returns a dict mapping top-level categories to their subcategories.
This helps the LLM choose existing paths rather than creating new ones.
Args:
user_namespace: User namespace (e.g., "users/jpmschweitzer")
limit: Maximum pages to fetch
Returns:
Dict like {"reference": ["political-entities", "tech"], "places": ["the-netherlands"]}
"""
from collections import defaultdict
pages = await self.list_pages(path_prefix=user_namespace, limit=limit)
# Extract structure: category -> set of subcategories
structure: Dict[str, set] = defaultdict(set)
prefix = user_namespace.strip("/") + "/"
for page in pages:
path = page.get("path", "")
if not path.startswith(prefix):
continue
# Get relative path within user namespace
rel_path = path[len(prefix):]
parts = rel_path.split("/")
if len(parts) >= 1:
category = parts[0]
if len(parts) >= 2:
# Has subcategory (e.g., reference/political-entities/nato)
structure[category].add(parts[1])
else:
# Just ensure category exists in structure
structure[category] # Access to ensure key exists
# Convert sets to sorted lists
return {cat: sorted(list(subs)) for cat, subs in sorted(structure.items())}
async def get_page(self, page_id: int) -> Optional[Dict[str, Any]]:
"""
Get single page by ID.