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>
167 lines
4.2 KiB
Python
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()
|