Files
HarborForge.Backend/app/schemas/knowledge.py
hzhang 9feff8e008 feat(knowledge-base): KnowledgeBase feature — models, CRUD API, RBAC
New entities mirroring the Project shape:
- knowledge_bases (human code, title, description, created_by, timestamps)
- knowledge_topics (UNIQUE(topic, knowledge_base_id))
- knowledge_categories (self-referential parent; UNIQUE(topic_id, parent, name),
  with an app-level check for the NULL-parent case MySQL can't enforce)
- knowledge_facts (category_id NULL → fact lives directly on the topic)
- project_knowledge_bases (M2M project ↔ knowledge base)

Adds full CRUD for KB/topic/category/fact, a nested /tree aggregate,
project link/unlink/list, KB-code generation (same algorithm as project
codes), and category cycle-prevention. Four global permissions
(knowledge-base.create/read/update/delete) seeded in init_bootstrap and
granted to admin/mgr/dev/general-agent/guest as appropriate. New tables
auto-create via Base.metadata.create_all; router wired in main.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 15:03:14 +01:00

167 lines
4.2 KiB
Python

"""Pydantic schemas for the Knowledge Base feature."""
from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime
# --------------------------------------------------------------------------
# Knowledge Base
# --------------------------------------------------------------------------
class KnowledgeBaseBase(BaseModel):
title: str
description: Optional[str] = None
class KnowledgeBaseCreate(KnowledgeBaseBase):
pass
class KnowledgeBaseUpdate(BaseModel):
title: Optional[str] = None
description: Optional[str] = None
class KnowledgeBaseResponse(BaseModel):
id: int
knowledge_base_code: Optional[str] = None
title: str
description: Optional[str] = None
created_by: int
created_at: Optional[datetime] = None
last_updated_at: Optional[datetime] = None
class Config:
from_attributes = True
# --------------------------------------------------------------------------
# Topic
# --------------------------------------------------------------------------
class KnowledgeTopicBase(BaseModel):
topic: str
description: Optional[str] = None
class KnowledgeTopicCreate(KnowledgeTopicBase):
pass
class KnowledgeTopicUpdate(BaseModel):
topic: Optional[str] = None
description: Optional[str] = None
class KnowledgeTopicResponse(BaseModel):
id: int
topic: str
knowledge_base_id: int
description: Optional[str] = None
created_by: int
created_at: Optional[datetime] = None
last_updated_at: Optional[datetime] = None
class Config:
from_attributes = True
# --------------------------------------------------------------------------
# Category
# --------------------------------------------------------------------------
class KnowledgeCategoryBase(BaseModel):
name: str
description: Optional[str] = None
class KnowledgeCategoryCreate(KnowledgeCategoryBase):
topic_id: int
parent: Optional[int] = None
class KnowledgeCategoryUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
parent: Optional[int] = None
class KnowledgeCategoryResponse(BaseModel):
id: int
name: str
parent: Optional[int] = None
topic_id: int
description: Optional[str] = None
created_by: Optional[int] = None
last_updated_by: Optional[int] = None
class Config:
from_attributes = True
# --------------------------------------------------------------------------
# Fact
# --------------------------------------------------------------------------
class KnowledgeFactBase(BaseModel):
fact: str
class KnowledgeFactCreate(KnowledgeFactBase):
topic_id: int
category_id: Optional[int] = None
class KnowledgeFactUpdate(BaseModel):
fact: Optional[str] = None
category_id: Optional[int] = None
class KnowledgeFactResponse(BaseModel):
id: int
category_id: Optional[int] = None
topic_id: int
fact: str
last_updated_at: Optional[datetime] = None
class Config:
from_attributes = True
# --------------------------------------------------------------------------
# Project <-> KnowledgeBase link
# --------------------------------------------------------------------------
class ProjectKnowledgeBaseLink(BaseModel):
# Accept either a numeric id or a knowledge_base_code (mirrors how
# projects are referenced elsewhere).
knowledge_base: str
# --------------------------------------------------------------------------
# Nested tree (read-only aggregate)
# --------------------------------------------------------------------------
class CategoryTreeNode(BaseModel):
id: int
name: str
parent: Optional[int] = None
topic_id: int
description: Optional[str] = None
categories: List["CategoryTreeNode"] = []
facts: List[KnowledgeFactResponse] = []
class TopicTreeNode(BaseModel):
id: int
topic: str
knowledge_base_id: int
description: Optional[str] = None
categories: List[CategoryTreeNode] = []
facts: List[KnowledgeFactResponse] = []
class KnowledgeBaseTree(BaseModel):
id: int
knowledge_base_code: Optional[str] = None
title: str
description: Optional[str] = None
topics: List[TopicTreeNode] = []
CategoryTreeNode.model_rebuild()