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>
This commit is contained in:
166
app/schemas/knowledge.py
Normal file
166
app/schemas/knowledge.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user