GT AI OS Community Edition v2.0.33
Security hardening release addressing CodeQL and Dependabot alerts: - Fix stack trace exposure in error responses - Add SSRF protection with DNS resolution checking - Implement proper URL hostname validation (replaces substring matching) - Add centralized path sanitization to prevent path traversal - Fix ReDoS vulnerability in email validation regex - Improve HTML sanitization in validation utilities - Fix capability wildcard matching in auth utilities - Update glob dependency to address CVE - Add CodeQL suppression comments for verified false positives 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
340
apps/tenant-backend/app/models/category.py
Normal file
340
apps/tenant-backend/app/models/category.py
Normal file
@@ -0,0 +1,340 @@
|
||||
"""
|
||||
Category Model for GT 2.0 Agent Discovery
|
||||
|
||||
Implements a simple hierarchical category system for organizing agents.
|
||||
Follows GT 2.0's principle of "Clarity Over Complexity"
|
||||
- Simple parent-child relationships
|
||||
- System categories that cannot be deleted
|
||||
- Tenant-specific and global categories
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, Any, List
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import Column, Integer, String, Text, DateTime, Boolean, ForeignKey
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.core.database import Base
|
||||
|
||||
|
||||
class Category(Base):
|
||||
"""Category model for organizing agents and resources
|
||||
|
||||
GT 2.0 Design: Simple hierarchical categories without complex taxonomies
|
||||
"""
|
||||
|
||||
__tablename__ = "categories"
|
||||
|
||||
# Primary Key
|
||||
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()), index=True)
|
||||
slug = Column(String(100), unique=True, nullable=False, index=True) # URL-safe identifier
|
||||
|
||||
# Category Details
|
||||
name = Column(String(100), nullable=False, index=True)
|
||||
display_name = Column(String(100), nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
icon = Column(String(10), nullable=True) # Emoji or icon code
|
||||
color = Column(String(20), nullable=True) # Hex color code for UI
|
||||
|
||||
# Hierarchy (simple parent-child)
|
||||
parent_id = Column(String(36), ForeignKey("categories.id"), nullable=True, index=True)
|
||||
|
||||
# Scope
|
||||
is_system = Column(Boolean, nullable=False, default=False) # Protected from deletion
|
||||
is_global = Column(Boolean, nullable=False, default=True) # Available to all tenants
|
||||
|
||||
# Display Order
|
||||
sort_order = Column(Integer, nullable=False, default=0)
|
||||
|
||||
# Usage Statistics (cached)
|
||||
assistant_count = Column(Integer, nullable=False, default=0)
|
||||
dataset_count = Column(Integer, nullable=False, default=0)
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
|
||||
|
||||
# Relationships
|
||||
parent = relationship("Category", remote_side=[id], backref="children")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Category(id={self.id}, name='{self.name}', slug='{self.slug}')>"
|
||||
|
||||
def to_dict(self, include_children: bool = False) -> Dict[str, Any]:
|
||||
"""Convert to dictionary for API responses"""
|
||||
data = {
|
||||
"id": self.id,
|
||||
"slug": self.slug,
|
||||
"name": self.name,
|
||||
"display_name": self.display_name,
|
||||
"description": self.description,
|
||||
"icon": self.icon,
|
||||
"color": self.color,
|
||||
"parent_id": self.parent_id,
|
||||
"is_system": self.is_system,
|
||||
"is_global": self.is_global,
|
||||
"sort_order": self.sort_order,
|
||||
"assistant_count": self.assistant_count,
|
||||
"dataset_count": self.dataset_count,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
}
|
||||
|
||||
if include_children and self.children:
|
||||
data["children"] = [child.to_dict() for child in self.children]
|
||||
|
||||
return data
|
||||
|
||||
def get_full_path(self) -> str:
|
||||
"""Get full category path (e.g., 'AI Tools > Research > Academic')"""
|
||||
if not self.parent_id:
|
||||
return self.display_name
|
||||
|
||||
# Simple recursion to build path
|
||||
parent_path = self.parent.get_full_path() if self.parent else ""
|
||||
return f"{parent_path} > {self.display_name}" if parent_path else self.display_name
|
||||
|
||||
def is_descendant_of(self, ancestor_id: int) -> bool:
|
||||
"""Check if this category is a descendant of another"""
|
||||
if not self.parent_id:
|
||||
return False
|
||||
|
||||
if self.parent_id == ancestor_id:
|
||||
return True
|
||||
|
||||
return self.parent.is_descendant_of(ancestor_id) if self.parent else False
|
||||
|
||||
def get_all_descendants(self) -> List["Category"]:
|
||||
"""Get all descendant categories"""
|
||||
descendants = []
|
||||
|
||||
if self.children:
|
||||
for child in self.children:
|
||||
descendants.append(child)
|
||||
descendants.extend(child.get_all_descendants())
|
||||
|
||||
return descendants
|
||||
|
||||
def update_counts(self, assistant_delta: int = 0, dataset_delta: int = 0) -> None:
|
||||
"""Update resource counts for this category"""
|
||||
self.assistant_count = max(0, self.assistant_count + assistant_delta)
|
||||
self.dataset_count = max(0, self.dataset_count + dataset_delta)
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
|
||||
# GT 2.0 Default System Categories
|
||||
DEFAULT_CATEGORIES = [
|
||||
# Top-level categories
|
||||
{
|
||||
"slug": "research",
|
||||
"name": "Research & Analysis",
|
||||
"display_name": "Research & Analysis",
|
||||
"description": "Agents for research, analysis, and information synthesis",
|
||||
"icon": "🔍",
|
||||
"color": "#3B82F6", # Blue
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 10,
|
||||
},
|
||||
{
|
||||
"slug": "development",
|
||||
"name": "Software Development",
|
||||
"display_name": "Software Development",
|
||||
"description": "Coding, debugging, and development tools",
|
||||
"icon": "💻",
|
||||
"color": "#10B981", # Green
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 20,
|
||||
},
|
||||
{
|
||||
"slug": "cybersecurity",
|
||||
"name": "Cybersecurity",
|
||||
"display_name": "Cybersecurity",
|
||||
"description": "Security analysis, threat detection, and incident response",
|
||||
"icon": "🛡️",
|
||||
"color": "#EF4444", # Red
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 30,
|
||||
},
|
||||
{
|
||||
"slug": "education",
|
||||
"name": "Education & Training",
|
||||
"display_name": "Education & Training",
|
||||
"description": "Educational agents and AI literacy tools",
|
||||
"icon": "🎓",
|
||||
"color": "#8B5CF6", # Purple
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 40,
|
||||
},
|
||||
{
|
||||
"slug": "creative",
|
||||
"name": "Creative & Content",
|
||||
"display_name": "Creative & Content",
|
||||
"description": "Writing, design, and creative content generation",
|
||||
"icon": "✨",
|
||||
"color": "#F59E0B", # Amber
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 50,
|
||||
},
|
||||
{
|
||||
"slug": "analytics",
|
||||
"name": "Data & Analytics",
|
||||
"display_name": "Data & Analytics",
|
||||
"description": "Data analysis, visualization, and insights",
|
||||
"icon": "📊",
|
||||
"color": "#06B6D4", # Cyan
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 60,
|
||||
},
|
||||
{
|
||||
"slug": "business",
|
||||
"name": "Business & Operations",
|
||||
"display_name": "Business & Operations",
|
||||
"description": "Business analysis, planning, and operations",
|
||||
"icon": "💼",
|
||||
"color": "#64748B", # Slate
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 70,
|
||||
},
|
||||
{
|
||||
"slug": "personal",
|
||||
"name": "Personal Productivity",
|
||||
"display_name": "Personal Productivity",
|
||||
"description": "Personal agents and productivity tools",
|
||||
"icon": "🚀",
|
||||
"color": "#14B8A6", # Teal
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 80,
|
||||
},
|
||||
{
|
||||
"slug": "custom",
|
||||
"name": "Custom & Specialized",
|
||||
"display_name": "Custom & Specialized",
|
||||
"description": "Custom-built and specialized agents",
|
||||
"icon": "⚙️",
|
||||
"color": "#71717A", # Zinc
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 90,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Sub-categories (examples)
|
||||
DEFAULT_SUBCATEGORIES = [
|
||||
# Research subcategories
|
||||
{
|
||||
"slug": "research-academic",
|
||||
"name": "Academic Research",
|
||||
"display_name": "Academic Research",
|
||||
"description": "Academic papers, citations, and literature review",
|
||||
"icon": "📚",
|
||||
"parent_slug": "research", # Will be resolved to parent_id
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 11,
|
||||
},
|
||||
{
|
||||
"slug": "research-market",
|
||||
"name": "Market Research",
|
||||
"display_name": "Market Research",
|
||||
"description": "Market analysis, competitor research, and trends",
|
||||
"icon": "📈",
|
||||
"parent_slug": "research",
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 12,
|
||||
},
|
||||
|
||||
# Development subcategories
|
||||
{
|
||||
"slug": "dev-web",
|
||||
"name": "Web Development",
|
||||
"display_name": "Web Development",
|
||||
"description": "Frontend, backend, and full-stack development",
|
||||
"icon": "🌐",
|
||||
"parent_slug": "development",
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 21,
|
||||
},
|
||||
{
|
||||
"slug": "dev-mobile",
|
||||
"name": "Mobile Development",
|
||||
"display_name": "Mobile Development",
|
||||
"description": "iOS, Android, and cross-platform development",
|
||||
"icon": "📱",
|
||||
"parent_slug": "development",
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 22,
|
||||
},
|
||||
{
|
||||
"slug": "dev-devops",
|
||||
"name": "DevOps & Infrastructure",
|
||||
"display_name": "DevOps & Infrastructure",
|
||||
"description": "CI/CD, containerization, and infrastructure",
|
||||
"icon": "🔧",
|
||||
"parent_slug": "development",
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 23,
|
||||
},
|
||||
|
||||
# Cybersecurity subcategories
|
||||
{
|
||||
"slug": "cyber-analysis",
|
||||
"name": "Threat Analysis",
|
||||
"display_name": "Threat Analysis",
|
||||
"description": "Threat detection, analysis, and intelligence",
|
||||
"icon": "🔍",
|
||||
"parent_slug": "cybersecurity",
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 31,
|
||||
},
|
||||
{
|
||||
"slug": "cyber-incident",
|
||||
"name": "Incident Response",
|
||||
"display_name": "Incident Response",
|
||||
"description": "Incident handling and forensics",
|
||||
"icon": "🚨",
|
||||
"parent_slug": "cybersecurity",
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 32,
|
||||
},
|
||||
|
||||
# Education subcategories
|
||||
{
|
||||
"slug": "edu-ai-literacy",
|
||||
"name": "AI Literacy",
|
||||
"display_name": "AI Literacy",
|
||||
"description": "Understanding and working with AI systems",
|
||||
"icon": "🤖",
|
||||
"parent_slug": "education",
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 41,
|
||||
},
|
||||
{
|
||||
"slug": "edu-critical-thinking",
|
||||
"name": "Critical Thinking",
|
||||
"display_name": "Critical Thinking",
|
||||
"description": "Logic, reasoning, and problem-solving",
|
||||
"icon": "🧠",
|
||||
"parent_slug": "education",
|
||||
"is_system": True,
|
||||
"is_global": True,
|
||||
"sort_order": 42,
|
||||
},
|
||||
]
|
||||
Reference in New Issue
Block a user