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:
HackWeasel
2025-12-12 17:04:45 -05:00
commit b9dfb86260
746 changed files with 232071 additions and 0 deletions

View File

@@ -0,0 +1,154 @@
"""
Agent schemas for GT 2.0 Tenant Backend
Pydantic models for agent-related API request/response validation.
Implements comprehensive agent management per CLAUDE.md specifications.
"""
from pydantic import BaseModel, Field, ConfigDict
from typing import List, Optional, Dict, Any
from datetime import datetime
from uuid import UUID
class AgentTemplate(BaseModel):
"""Agent template information"""
id: str = Field(..., description="Template identifier")
name: str = Field(..., description="Template display name")
description: str = Field(..., description="Template description")
icon: str = Field(..., description="Template icon emoji or URL")
category: str = Field(..., description="Template category")
prompt: str = Field(..., description="System prompt template")
default_capabilities: List[str] = Field(default_factory=list, description="Default capability grants")
personality_config: Dict[str, Any] = Field(default_factory=dict, description="Personality configuration")
resource_preferences: Dict[str, Any] = Field(default_factory=dict, description="Resource preferences")
class AgentTemplateListResponse(BaseModel):
"""Response for listing agent templates"""
templates: List[AgentTemplate]
categories: List[str] = Field(default_factory=list, description="Available categories")
total: int
class AgentCreate(BaseModel):
"""Request to create a new agent"""
name: str = Field(..., description="Agent name")
description: Optional[str] = Field(None, description="Agent description")
template_id: Optional[str] = Field(None, description="Template ID to use")
category: Optional[str] = Field(None, description="Agent category")
prompt_template: Optional[str] = Field(None, description="System prompt template")
model: Optional[str] = Field(None, description="AI model identifier")
model_id: Optional[str] = Field(None, description="AI model identifier (alias for model)")
temperature: Optional[float] = Field(None, description="Model temperature parameter")
# max_tokens removed - now determined by model configuration
visibility: Optional[str] = Field(None, description="Agent visibility setting")
dataset_connection: Optional[str] = Field(None, description="RAG dataset connection type")
selected_dataset_ids: Optional[List[str]] = Field(None, description="Selected dataset IDs for RAG")
personality_config: Optional[Dict[str, Any]] = Field(None, description="Personality configuration")
resource_preferences: Optional[Dict[str, Any]] = Field(None, description="Resource preferences")
tags: Optional[List[str]] = Field(None, description="Agent tags")
disclaimer: Optional[str] = Field(None, max_length=500, description="Disclaimer text shown in chat")
easy_prompts: Optional[List[str]] = Field(None, description="Quick-access preset prompts (max 10)")
team_shares: Optional[List[Dict[str, Any]]] = Field(None, description="Team sharing configuration with per-user permissions")
model_config = ConfigDict(protected_namespaces=())
class AgentUpdate(BaseModel):
"""Request to update an agent"""
name: Optional[str] = Field(None, description="New agent name")
description: Optional[str] = Field(None, description="New agent description")
category: Optional[str] = Field(None, description="Agent category")
prompt_template: Optional[str] = Field(None, description="System prompt template")
model: Optional[str] = Field(None, description="AI model identifier")
temperature: Optional[float] = Field(None, description="Model temperature parameter")
# max_tokens removed - now determined by model configuration
visibility: Optional[str] = Field(None, description="Agent visibility setting")
dataset_connection: Optional[str] = Field(None, description="RAG dataset connection type")
selected_dataset_ids: Optional[List[str]] = Field(None, description="Selected dataset IDs for RAG")
personality_config: Optional[Dict[str, Any]] = Field(None, description="Updated personality config")
resource_preferences: Optional[Dict[str, Any]] = Field(None, description="Updated resource preferences")
tags: Optional[List[str]] = Field(None, description="Updated tags")
is_favorite: Optional[bool] = Field(None, description="Favorite status")
disclaimer: Optional[str] = Field(None, max_length=500, description="Disclaimer text shown in chat")
easy_prompts: Optional[List[str]] = Field(None, description="Quick-access preset prompts (max 10)")
team_shares: Optional[List[Dict[str, Any]]] = Field(None, description="Update team sharing configuration")
class AgentResponse(BaseModel):
"""Response for agent operations"""
id: str = Field(..., description="Agent UUID")
name: str = Field(..., description="Agent name")
description: Optional[str] = Field(None, description="Agent description")
template_id: Optional[str] = Field(None, description="Template ID if created from template")
category: Optional[str] = Field(None, description="Agent category")
prompt_template: Optional[str] = Field(None, description="System prompt template")
model: Optional[str] = Field(None, description="AI model identifier")
temperature: Optional[float] = Field(None, description="Model temperature parameter")
max_tokens: Optional[int] = Field(None, description="Maximum tokens for generation")
visibility: Optional[str] = Field(None, description="Agent visibility setting")
dataset_connection: Optional[str] = Field(None, description="RAG dataset connection type")
selected_dataset_ids: Optional[List[str]] = Field(None, description="Selected dataset IDs for RAG")
personality_config: Dict[str, Any] = Field(default_factory=dict, description="Personality configuration")
resource_preferences: Dict[str, Any] = Field(default_factory=dict, description="Resource preferences")
tags: List[str] = Field(default_factory=list, description="Agent tags")
is_favorite: bool = Field(False, description="Favorite status")
disclaimer: Optional[str] = Field(None, description="Disclaimer text shown in chat")
easy_prompts: List[str] = Field(default_factory=list, description="Quick-access preset prompts")
conversation_count: int = Field(0, description="Number of conversations")
usage_count: int = Field(0, description="Number of conversations (alias for frontend compatibility)")
total_cost_cents: int = Field(0, description="Total cost in cents")
created_at: datetime = Field(..., description="Creation timestamp")
updated_at: datetime = Field(..., description="Last update timestamp")
# Creator information
created_by_name: Optional[str] = Field(None, description="Full name of the user who created this agent")
# Permission flags for frontend
can_edit: bool = Field(False, description="Whether current user can edit this agent")
can_delete: bool = Field(False, description="Whether current user can delete this agent")
is_owner: bool = Field(False, description="Whether current user owns this agent")
# Team sharing configuration
team_shares: Optional[List[Dict[str, Any]]] = Field(None, description="Team sharing configuration with per-user permissions")
model_config = ConfigDict(from_attributes=True)
class AgentListResponse(BaseModel):
"""Response for listing agents"""
data: List[AgentResponse] = Field(..., description="List of agents")
total: int = Field(..., description="Total number of agents")
limit: int = Field(..., description="Query limit")
offset: int = Field(..., description="Query offset")
class AgentCapabilities(BaseModel):
"""Agent capabilities and resource access"""
agent_id: str = Field(..., description="Agent UUID")
capabilities: List[Dict[str, Any]] = Field(default_factory=list, description="Granted capabilities")
resource_preferences: Dict[str, Any] = Field(default_factory=dict, description="Resource preferences")
allowed_tools: List[str] = Field(default_factory=list, description="Allowed tool integrations")
total: int = Field(..., description="Total capability count")
class AgentStatistics(BaseModel):
"""Agent usage statistics"""
agent_id: str = Field(..., description="Agent UUID")
name: str = Field(..., description="Agent name")
created_at: datetime = Field(..., description="Creation timestamp")
last_used_at: Optional[datetime] = Field(None, description="Last usage timestamp")
conversation_count: int = Field(0, description="Total conversations")
total_messages: int = Field(0, description="Total messages processed")
total_tokens_used: int = Field(0, description="Total tokens consumed")
total_cost_cents: int = Field(0, description="Total cost in cents")
total_cost_dollars: float = Field(0.0, description="Total cost in dollars")
average_tokens_per_message: float = Field(0.0, description="Average tokens per message")
is_favorite: bool = Field(False, description="Favorite status")
tags: List[str] = Field(default_factory=list, description="Agent tags")
model_config = ConfigDict(from_attributes=True)
class AgentCloneRequest(BaseModel):
"""Request to clone an agent"""
new_name: str = Field(..., description="Name for the cloned agent")
modifications: Optional[Dict[str, Any]] = Field(None, description="Modifications to apply")

View File

@@ -0,0 +1,71 @@
"""
Category schemas for GT 2.0 Tenant Backend
Pydantic models for agent category API request/response validation.
Supports tenant-scoped editable/deletable categories per Issue #215.
"""
from pydantic import BaseModel, Field, field_validator
from typing import List, Optional
from datetime import datetime
import re
class CategoryCreate(BaseModel):
"""Request to create a new category"""
name: str = Field(..., min_length=1, max_length=100, description="Category display name")
description: Optional[str] = Field(None, max_length=500, description="Category description")
icon: Optional[str] = Field(None, max_length=10, description="Category icon (emoji)")
@field_validator('name')
@classmethod
def validate_name(cls, v: str) -> str:
v = v.strip()
if not v:
raise ValueError('Category name cannot be empty')
# Check for invalid characters (allow alphanumeric, spaces, hyphens, underscores)
if not re.match(r'^[\w\s\-]+$', v):
raise ValueError('Category name can only contain letters, numbers, spaces, hyphens, and underscores')
return v
class CategoryUpdate(BaseModel):
"""Request to update a category"""
name: Optional[str] = Field(None, min_length=1, max_length=100, description="New category name")
description: Optional[str] = Field(None, max_length=500, description="New category description")
icon: Optional[str] = Field(None, max_length=10, description="New category icon")
@field_validator('name')
@classmethod
def validate_name(cls, v: Optional[str]) -> Optional[str]:
if v is None:
return v
v = v.strip()
if not v:
raise ValueError('Category name cannot be empty')
if not re.match(r'^[\w\s\-]+$', v):
raise ValueError('Category name can only contain letters, numbers, spaces, hyphens, and underscores')
return v
class CategoryResponse(BaseModel):
"""Response for category operations"""
id: str = Field(..., description="Category UUID")
name: str = Field(..., description="Category display name")
slug: str = Field(..., description="URL-safe category identifier")
description: Optional[str] = Field(None, description="Category description")
icon: Optional[str] = Field(None, description="Category icon (emoji)")
is_default: bool = Field(..., description="Whether this is a system default category")
created_by: Optional[str] = Field(None, description="UUID of user who created the category")
created_by_name: Optional[str] = Field(None, description="Name of user who created the category")
can_edit: bool = Field(..., description="Whether current user can edit this category")
can_delete: bool = Field(..., description="Whether current user can delete this category")
sort_order: int = Field(..., description="Display sort order")
created_at: datetime = Field(..., description="Creation timestamp")
updated_at: datetime = Field(..., description="Last update timestamp")
class CategoryListResponse(BaseModel):
"""Response for listing categories"""
categories: List[CategoryResponse] = Field(default_factory=list, description="List of categories")
total: int = Field(..., description="Total number of categories")

View File

@@ -0,0 +1,81 @@
"""
Conversation schemas for GT 2.0 Tenant Backend
Pydantic models for conversation-related API request/response validation.
"""
from pydantic import BaseModel, Field, ConfigDict
from typing import List, Optional, Dict, Any
from datetime import datetime
class ConversationCreate(BaseModel):
"""Request to create a new conversation"""
agent_id: str = Field(..., description="Agent UUID to chat with")
title: Optional[str] = Field(None, description="Conversation title")
initial_message: Optional[str] = Field(None, description="First message to send")
class ConversationUpdate(BaseModel):
"""Request to update a conversation"""
title: Optional[str] = Field(None, description="New conversation title")
system_prompt: Optional[str] = Field(None, description="Updated system prompt")
class MessageCreate(BaseModel):
"""Request to send a message"""
content: str = Field(..., description="Message content")
context_sources: Optional[List[str]] = Field(None, description="Context source IDs")
metadata: Optional[Dict[str, Any]] = Field(None, description="Additional message metadata")
class MessageResponse(BaseModel):
"""Message response"""
id: Optional[str] = Field(None, description="Message ID")
message_id: Optional[str] = Field(None, description="Message ID (alternative)")
content: Optional[str] = Field(None, description="Message content")
role: Optional[str] = Field(None, description="Message role")
tokens_used: Optional[int] = Field(None, description="Tokens consumed")
model_used: Optional[str] = Field(None, description="Model used for generation")
context_sources: Optional[List[str]] = Field(None, description="RAG context source documents")
created_at: Optional[datetime] = Field(None, description="Creation timestamp")
stream: Optional[bool] = Field(None, description="Whether response is streamed")
stream_endpoint: Optional[str] = Field(None, description="Stream endpoint URL")
model_config = ConfigDict(from_attributes=True, protected_namespaces=())
class MessageListResponse(BaseModel):
"""Response for listing messages"""
messages: List[MessageResponse]
conversation_id: int
total: int
class ConversationResponse(BaseModel):
"""Conversation response"""
id: int = Field(..., description="Conversation ID")
title: str = Field(..., description="Conversation title")
agent_id: str = Field(..., description="Agent ID")
model_id: str = Field(..., description="Model identifier")
system_prompt: Optional[str] = Field(None, description="System prompt")
message_count: int = Field(0, description="Total message count")
total_tokens: int = Field(0, description="Total tokens used")
created_at: datetime = Field(..., description="Creation timestamp")
updated_at: datetime = Field(..., description="Last update timestamp")
messages: Optional[List[MessageResponse]] = Field(None, description="Conversation messages")
model_config = ConfigDict(from_attributes=True, protected_namespaces=())
class ConversationWithUnread(ConversationResponse):
"""Conversation response with unread message count"""
unread_count: int = Field(0, description="Number of unread messages")
class ConversationListResponse(BaseModel):
"""Response for listing conversations"""
conversations: List[ConversationResponse]
total: int
limit: int
offset: int

View File

@@ -0,0 +1,160 @@
"""
Document Pydantic schemas for GT 2.0 Tenant Backend
Defines request/response schemas for document and RAG operations.
"""
from datetime import datetime
from typing import Optional, List, Dict, Any
from pydantic import BaseModel, Field, validator
class DocumentResponse(BaseModel):
"""Document response schema"""
id: int
uuid: str
filename: str
original_filename: str
file_type: str
file_extension: str
file_size_bytes: int
processing_status: str
chunk_count: int
content_summary: Optional[str] = None
detected_language: Optional[str] = None
content_type: Optional[str] = None
keywords: List[str] = Field(default_factory=list)
uploaded_by: str
tags: List[str] = Field(default_factory=list)
category: Optional[str] = None
access_count: int = 0
is_active: bool = True
is_searchable: bool = True
created_at: datetime
updated_at: datetime
processed_at: Optional[datetime] = None
last_accessed_at: Optional[datetime] = None
class Config:
from_attributes = True
class RAGDatasetCreate(BaseModel):
"""Schema for creating a RAG dataset"""
dataset_name: str = Field(..., min_length=1, max_length=255)
description: Optional[str] = Field(None, max_length=1000)
chunking_strategy: str = Field(default="hybrid", pattern="^(fixed|semantic|hierarchical|hybrid)$")
chunk_size: int = Field(default=512, ge=128, le=2048)
chunk_overlap: int = Field(default=128, ge=0, le=512)
embedding_model: str = Field(default="BAAI/bge-m3")
@validator('chunk_overlap')
def validate_chunk_overlap(cls, v, values):
if 'chunk_size' in values and v >= values['chunk_size']:
raise ValueError('chunk_overlap must be less than chunk_size')
return v
class RAGDatasetResponse(BaseModel):
"""RAG dataset response schema"""
id: str
user_id: str
dataset_name: str
description: Optional[str] = None
chunking_strategy: str
embedding_model: str
chunk_size: int
chunk_overlap: int
document_count: int = 0
chunk_count: int = 0
vector_count: int = 0
total_size_bytes: int = 0
status: str
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class DocumentChunkResponse(BaseModel):
"""Document chunk response schema"""
id: str
chunk_index: int
chunk_metadata: Dict[str, Any] = Field(default_factory=dict)
embedding_id: str
created_at: datetime
class Config:
from_attributes = True
class SearchRequest(BaseModel):
"""Document search request schema"""
query: str = Field(..., min_length=1, max_length=1000)
dataset_ids: Optional[List[str]] = None
top_k: int = Field(default=5, ge=1, le=20)
similarity_threshold: float = Field(default=0.7, ge=0.0, le=1.0)
class SearchResult(BaseModel):
"""Document search result schema"""
document_id: Optional[int] = None
dataset_id: Optional[str] = None
dataset_name: Optional[str] = None
text: str
similarity: float
metadata: Dict[str, Any] = Field(default_factory=dict)
filename: Optional[str] = None
chunk_index: Optional[int] = None
class SearchResponse(BaseModel):
"""Document search response schema"""
query: str
results: List[SearchResult]
total_results: int
search_time_ms: Optional[float] = None
class DocumentContextResponse(BaseModel):
"""Document context response schema"""
document_id: int
document_name: str
query: str
relevant_chunks: List[SearchResult]
context_text: str
class RAGStatistics(BaseModel):
"""RAG usage statistics schema"""
user_id: str
document_count: int
dataset_count: int
total_size_bytes: int
total_size_mb: float
total_chunks: int
processed_documents: int
pending_documents: int
failed_documents: int
class ProcessDocumentRequest(BaseModel):
"""Document processing request schema"""
chunking_strategy: Optional[str] = Field(default="hybrid", pattern="^(fixed|semantic|hierarchical|hybrid)$")
class ProcessDocumentResponse(BaseModel):
"""Document processing response schema"""
status: str
document_id: int
chunk_count: int
vector_store_ids: List[str]
processing_time_ms: Optional[float] = None
class UploadDocumentResponse(BaseModel):
"""Document upload response schema"""
document: DocumentResponse
processing_initiated: bool = False
message: str = "Document uploaded successfully"

View File

@@ -0,0 +1,269 @@
"""
Event Pydantic schemas for GT 2.0 Tenant Backend
Defines request/response schemas for event automation operations.
"""
from datetime import datetime
from typing import Optional, List, Dict, Any
from pydantic import BaseModel, Field, validator
class EventActionCreate(BaseModel):
"""Schema for creating an event action"""
action_type: str = Field(..., description="Type of action to execute")
config: Dict[str, Any] = Field(default_factory=dict, description="Action configuration")
delay_seconds: int = Field(default=0, ge=0, le=3600, description="Delay before execution")
retry_count: int = Field(default=3, ge=0, le=10, description="Number of retries on failure")
retry_delay: int = Field(default=60, ge=1, le=3600, description="Delay between retries")
condition: Optional[str] = Field(None, max_length=1000, description="Python expression for conditional execution")
execution_order: int = Field(default=0, ge=0, description="Order of execution within subscription")
@validator('action_type')
def validate_action_type(cls, v):
valid_types = [
'process_document', 'send_notification', 'update_statistics',
'trigger_rag_indexing', 'log_analytics', 'execute_webhook',
'create_assistant', 'schedule_task'
]
if v not in valid_types:
raise ValueError(f'action_type must be one of: {", ".join(valid_types)}')
return v
class EventActionResponse(BaseModel):
"""Event action response schema"""
id: str
action_type: str
subscription_id: str
config: Dict[str, Any]
condition: Optional[str] = None
delay_seconds: int
retry_count: int
retry_delay: int
execution_order: int
is_active: bool
execution_count: int
success_count: int
failure_count: int
last_executed_at: Optional[datetime] = None
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class EventSubscriptionCreate(BaseModel):
"""Schema for creating an event subscription"""
name: str = Field(..., min_length=1, max_length=255)
description: Optional[str] = Field(None, max_length=1000)
event_type: str = Field(..., description="Type of event to subscribe to")
actions: List[EventActionCreate] = Field(..., min_items=1, description="Actions to execute")
filter_conditions: Dict[str, Any] = Field(default_factory=dict, description="Conditions for subscription activation")
@validator('event_type')
def validate_event_type(cls, v):
valid_types = [
'document.uploaded', 'document.processed', 'document.failed',
'conversation.started', 'message.sent', 'agent.created',
'rag.search_performed', 'user.login', 'user.activity',
'system.health_check'
]
if v not in valid_types:
raise ValueError(f'event_type must be one of: {", ".join(valid_types)}')
return v
class EventSubscriptionResponse(BaseModel):
"""Event subscription response schema"""
id: str
name: str
description: Optional[str] = None
event_type: str
user_id: str
tenant_id: str
trigger_id: Optional[str] = None
filter_conditions: Dict[str, Any]
is_active: bool
trigger_count: int
last_triggered_at: Optional[datetime] = None
created_at: datetime
updated_at: datetime
actions: List[EventActionResponse] = Field(default_factory=list)
class Config:
from_attributes = True
class EventResponse(BaseModel):
"""Event response schema"""
id: int
event_id: str
event_type: str
user_id: str
tenant_id: str
payload: Dict[str, Any]
metadata: Dict[str, Any]
status: str
error_message: Optional[str] = None
retry_count: int
created_at: datetime
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
class Config:
from_attributes = True
class EventStatistics(BaseModel):
"""Event statistics response schema"""
total_events: int
events_by_type: Dict[str, int]
events_by_status: Dict[str, int]
average_events_per_day: float
class EventTriggerCreate(BaseModel):
"""Schema for creating an event trigger"""
name: str = Field(..., min_length=1, max_length=255)
description: Optional[str] = Field(None, max_length=1000)
trigger_type: str = Field(..., description="Type of trigger")
config: Dict[str, Any] = Field(default_factory=dict, description="Trigger configuration")
conditions: Dict[str, Any] = Field(default_factory=dict, description="Trigger conditions")
@validator('trigger_type')
def validate_trigger_type(cls, v):
valid_types = [
'schedule', 'webhook', 'file_watch', 'database_change',
'api_call', 'user_action', 'system_event'
]
if v not in valid_types:
raise ValueError(f'trigger_type must be one of: {", ".join(valid_types)}')
return v
class EventTriggerResponse(BaseModel):
"""Event trigger response schema"""
id: str
name: str
description: Optional[str] = None
trigger_type: str
user_id: str
tenant_id: str
config: Dict[str, Any]
conditions: Dict[str, Any]
is_active: bool
created_at: datetime
updated_at: datetime
last_triggered_at: Optional[datetime] = None
class Config:
from_attributes = True
class ScheduledTaskResponse(BaseModel):
"""Scheduled task response schema"""
id: str
task_type: str
name: str
description: Optional[str] = None
scheduled_at: datetime
executed_at: Optional[datetime] = None
config: Dict[str, Any]
context: Dict[str, Any]
status: str
result: Optional[Dict[str, Any]] = None
error_message: Optional[str] = None
user_id: str
tenant_id: str
retry_count: int
max_retries: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class EventLogResponse(BaseModel):
"""Event log response schema"""
id: int
event_id: str
log_level: str
message: str
details: Dict[str, Any]
action_id: Optional[str] = None
subscription_id: Optional[str] = None
user_id: str
tenant_id: str
created_at: datetime
class Config:
from_attributes = True
class EmitEventRequest(BaseModel):
"""Request schema for manually emitting events"""
event_type: str = Field(..., description="Type of event to emit")
data: Dict[str, Any] = Field(..., description="Event data payload")
metadata: Optional[Dict[str, Any]] = Field(default_factory=dict, description="Additional metadata")
@validator('event_type')
def validate_event_type(cls, v):
valid_types = [
'document.uploaded', 'document.processed', 'document.failed',
'conversation.started', 'message.sent', 'agent.created',
'rag.search_performed', 'user.login', 'user.activity',
'system.health_check'
]
if v not in valid_types:
raise ValueError(f'event_type must be one of: {", ".join(valid_types)}')
return v
class WebhookConfig(BaseModel):
"""Configuration for webhook actions"""
url: str = Field(..., description="Webhook URL")
method: str = Field(default="POST", pattern="^(GET|POST|PUT|PATCH|DELETE)$")
headers: Dict[str, str] = Field(default_factory=dict)
timeout: int = Field(default=30, ge=1, le=300)
retry_on_failure: bool = Field(default=True)
class NotificationConfig(BaseModel):
"""Configuration for notification actions"""
type: str = Field(default="system", description="Notification type")
message: str = Field(..., min_length=1, max_length=1000, description="Notification message")
priority: str = Field(default="normal", pattern="^(low|normal|high|urgent)$")
channels: List[str] = Field(default_factory=list, description="Notification channels")
class DocumentProcessingConfig(BaseModel):
"""Configuration for document processing actions"""
chunking_strategy: str = Field(default="hybrid", pattern="^(fixed|semantic|hierarchical|hybrid)$")
chunk_size: int = Field(default=512, ge=128, le=2048)
chunk_overlap: int = Field(default=128, ge=0, le=512)
auto_index: bool = Field(default=True, description="Automatically index in RAG system")
class StatisticsUpdateConfig(BaseModel):
"""Configuration for statistics update actions"""
type: str = Field(..., description="Type of statistic to update")
increment: int = Field(default=1, description="Amount to increment")
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
class AssistantCreationConfig(BaseModel):
"""Configuration for agent creation actions"""
template_id: str = Field(default="general_assistant", description="Agent template ID")
name: str = Field(..., min_length=1, max_length=255, description="Agent name")
config_overrides: Dict[str, Any] = Field(default_factory=dict, description="Configuration overrides")
class TaskSchedulingConfig(BaseModel):
"""Configuration for task scheduling actions"""
task_type: str = Field(..., description="Type of task to schedule")
delay_minutes: int = Field(default=0, ge=0, description="Delay before execution")
task_config: Dict[str, Any] = Field(default_factory=dict, description="Task configuration")
max_retries: int = Field(default=3, ge=0, le=10, description="Maximum retry attempts")

View File

@@ -0,0 +1,64 @@
"""
User schemas for GT 2.0 Tenant Backend
Pydantic models for user-related API request/response validation.
Implements user preferences management per GT 2.0 specifications.
"""
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
from datetime import datetime
class CustomCategory(BaseModel):
"""User-defined custom category with metadata"""
name: str = Field(..., description="Category name (lowercase, unique per user)")
description: str = Field(..., description="Category description")
created_at: Optional[str] = Field(None, description="ISO timestamp when category was created")
class UserPreferences(BaseModel):
"""User preferences stored in JSONB"""
favorite_agent_ids: Optional[List[str]] = Field(default_factory=list, description="List of favorited agent UUIDs")
custom_categories: Optional[List[CustomCategory]] = Field(default_factory=list, description="User's custom agent categories")
# Future preferences can be added here
class UserPreferencesResponse(BaseModel):
"""Response for getting user preferences"""
preferences: Dict[str, Any] = Field(..., description="User preferences dictionary")
class UpdateUserPreferencesRequest(BaseModel):
"""Request to update user preferences (merges with existing)"""
preferences: Dict[str, Any] = Field(..., description="Preferences to merge with existing")
class FavoriteAgentsResponse(BaseModel):
"""Response for getting favorite agent IDs"""
favorite_agent_ids: List[str] = Field(..., description="List of favorited agent UUIDs")
class UpdateFavoriteAgentsRequest(BaseModel):
"""Request to update favorite agent IDs (replaces existing list)"""
agent_ids: List[str] = Field(..., description="List of agent UUIDs to set as favorites")
class AddFavoriteAgentRequest(BaseModel):
"""Request to add a single agent to favorites"""
agent_id: str = Field(..., description="Agent UUID to add to favorites")
class RemoveFavoriteAgentRequest(BaseModel):
"""Request to remove a single agent from favorites"""
agent_id: str = Field(..., description="Agent UUID to remove from favorites")
class CustomCategoriesResponse(BaseModel):
"""Response for getting custom categories"""
categories: List[CustomCategory] = Field(..., description="List of user's custom categories")
class UpdateCustomCategoriesRequest(BaseModel):
"""Request to update custom categories (replaces entire list)"""
categories: List[CustomCategory] = Field(..., description="Complete list of custom categories")