- Updated python_coding_microproject.csv to use NVIDIA NIM Kimi K2 - Updated kali_linux_shell_simulator.csv to use NVIDIA NIM Kimi K2 - Made more general-purpose (flexible targets, expanded tools) - Added nemotron-mini-agent.csv for fast local inference via Ollama - Added nemotron-agent.csv for advanced reasoning via Ollama - Added wiki page: Projects for NVIDIA NIMs and Nemotron
264 lines
9.1 KiB
Python
264 lines
9.1 KiB
Python
"""
|
|
Collaboration Team Models for GT 2.0 Tenant Backend
|
|
|
|
Pydantic models for user collaboration teams (team sharing system).
|
|
This is separate from the tenant isolation 'tenants' table (formerly 'teams').
|
|
|
|
Database Schema:
|
|
- teams: User collaboration groups within a tenant
|
|
- team_memberships: Team members with two-tier permissions
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import List, Optional, Dict, Any
|
|
from pydantic import BaseModel, Field, ConfigDict, field_validator
|
|
|
|
|
|
class TeamBase(BaseModel):
|
|
"""Base team model with common fields"""
|
|
name: str = Field(..., min_length=1, max_length=255, description="Team name")
|
|
description: Optional[str] = Field(None, description="Team description")
|
|
|
|
|
|
class TeamCreate(TeamBase):
|
|
"""Model for creating a new team"""
|
|
pass
|
|
|
|
|
|
class TeamUpdate(BaseModel):
|
|
"""Model for updating a team"""
|
|
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
|
description: Optional[str] = None
|
|
|
|
|
|
class TeamMember(BaseModel):
|
|
"""Team member with permissions"""
|
|
id: str = Field(..., description="Membership UUID")
|
|
team_id: str = Field(..., description="Team UUID")
|
|
user_id: str = Field(..., description="User UUID")
|
|
user_email: str = Field(..., description="User email")
|
|
user_name: str = Field(..., description="User display name")
|
|
team_permission: str = Field(..., description="Team-level permission: 'read', 'share', or 'manager'")
|
|
resource_permissions: Dict[str, str] = Field(default_factory=dict, description="Resource-level permissions JSONB")
|
|
is_owner: bool = Field(default=False, description="Whether this member is the team owner")
|
|
is_observable: bool = Field(default=False, description="Member consents to activity observation")
|
|
observable_consent_status: str = Field(default="none", description="Consent status: 'none', 'pending', 'approved', 'revoked'")
|
|
observable_consent_at: Optional[str] = Field(None, description="When Observable status was approved")
|
|
status: str = Field(default="accepted", description="Membership status: 'pending', 'accepted', or 'declined'")
|
|
invited_at: Optional[str] = None
|
|
responded_at: Optional[str] = None
|
|
joined_at: Optional[str] = None
|
|
created_at: Optional[str] = None
|
|
updated_at: Optional[str] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class Team(TeamBase):
|
|
"""Complete team model with metadata"""
|
|
id: str = Field(..., description="Team UUID")
|
|
tenant_id: str = Field(..., description="Tenant UUID")
|
|
owner_id: str = Field(..., description="Owner user UUID")
|
|
owner_name: Optional[str] = Field(None, description="Owner display name")
|
|
owner_email: Optional[str] = Field(None, description="Owner email")
|
|
is_owner: bool = Field(..., description="Whether current user is the owner")
|
|
can_manage: bool = Field(..., description="Whether current user can manage the team")
|
|
user_permission: Optional[str] = Field(None, description="Current user's team permission: 'read' or 'share' (None if owner)")
|
|
member_count: int = Field(0, description="Number of team members")
|
|
shared_resource_count: int = Field(0, description="Number of shared resources (agents and datasets)")
|
|
created_at: Optional[str] = None
|
|
updated_at: Optional[str] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class TeamWithMembers(Team):
|
|
"""Team with full member list"""
|
|
members: List[TeamMember] = Field(default_factory=list, description="List of team members")
|
|
|
|
|
|
class TeamListResponse(BaseModel):
|
|
"""Response model for listing teams"""
|
|
data: List[Team]
|
|
total: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class TeamResponse(BaseModel):
|
|
"""Response model for single team operation"""
|
|
data: Team
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class TeamWithMembersResponse(BaseModel):
|
|
"""Response model for team with members"""
|
|
data: TeamWithMembers
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# Team Membership Models
|
|
|
|
class AddMemberRequest(BaseModel):
|
|
"""Request model for adding a member to a team"""
|
|
user_email: str = Field(..., description="Email of user to add")
|
|
team_permission: str = Field("read", description="Team permission: 'read', 'share', or 'manager'")
|
|
|
|
|
|
class UpdateMemberPermissionRequest(BaseModel):
|
|
"""Request model for updating member permission"""
|
|
team_permission: str = Field(..., description="New permission: 'read', 'share', or 'manager'")
|
|
|
|
@field_validator('team_permission')
|
|
@classmethod
|
|
def validate_permission(cls, v: str) -> str:
|
|
if v not in ["read", "share", "manager"]:
|
|
raise ValueError(f"Invalid permission: {v}. Must be 'read', 'share', or 'manager'")
|
|
return v
|
|
|
|
|
|
class MemberListResponse(BaseModel):
|
|
"""Response model for listing team members"""
|
|
data: List[TeamMember]
|
|
total: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class MemberResponse(BaseModel):
|
|
"""Response model for single member operation"""
|
|
data: TeamMember
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# Team Invitation Models
|
|
|
|
class TeamInvitation(BaseModel):
|
|
"""Pending team invitation"""
|
|
id: str = Field(..., description="Invitation (membership) UUID")
|
|
team_id: str = Field(..., description="Team UUID")
|
|
team_name: str = Field(..., description="Team name")
|
|
team_description: Optional[str] = Field(None, description="Team description")
|
|
owner_name: str = Field(..., description="Team owner display name")
|
|
owner_email: str = Field(..., description="Team owner email")
|
|
team_permission: str = Field(..., description="Invited permission: 'read', 'share', or 'manager'")
|
|
observable_requested: bool = Field(default=False, description="Whether Observable access was requested on invite")
|
|
invited_at: str = Field(..., description="Invitation timestamp")
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class InvitationActionRequest(BaseModel):
|
|
"""Request to accept or decline invitation"""
|
|
action: str = Field(..., description="Action: 'accept' or 'decline'")
|
|
|
|
|
|
class InvitationListResponse(BaseModel):
|
|
"""Response model for listing invitations"""
|
|
data: List[TeamInvitation]
|
|
total: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# Resource Sharing Models
|
|
|
|
class ShareResourceRequest(BaseModel):
|
|
"""Request model for sharing a resource to team"""
|
|
resource_type: str = Field(..., description="Resource type: 'agent' or 'dataset'")
|
|
resource_id: str = Field(..., description="Resource UUID")
|
|
user_permissions: Dict[str, str] = Field(
|
|
...,
|
|
description="User permissions: {user_id: 'read'|'edit'}"
|
|
)
|
|
|
|
|
|
class SharedResource(BaseModel):
|
|
"""Model for a shared resource"""
|
|
resource_type: str = Field(..., description="Resource type: 'agent' or 'dataset'")
|
|
resource_id: str = Field(..., description="Resource UUID")
|
|
resource_name: str = Field(..., description="Resource name")
|
|
resource_owner: str = Field(..., description="Resource owner name or email")
|
|
user_permissions: Dict[str, str] = Field(..., description="User permissions map")
|
|
|
|
|
|
class SharedResourcesResponse(BaseModel):
|
|
"""Response model for listing shared resources"""
|
|
data: List[SharedResource]
|
|
total: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# Observable Request Models
|
|
|
|
class ObservableRequest(BaseModel):
|
|
"""Observable access request for a team member"""
|
|
team_id: str = Field(..., description="Team UUID")
|
|
team_name: str = Field(..., description="Team name")
|
|
requested_by_name: str = Field(..., description="Name of manager/owner who requested")
|
|
requested_by_email: str = Field(..., description="Email of manager/owner who requested")
|
|
requested_at: str = Field(..., description="When request was made")
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class ObservableRequestListResponse(BaseModel):
|
|
"""Response model for listing Observable requests"""
|
|
data: List[ObservableRequest]
|
|
total: int
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# Team Activity Models
|
|
|
|
class TeamActivityMetrics(BaseModel):
|
|
"""Team activity metrics for Observable members"""
|
|
team_id: str
|
|
team_name: str
|
|
date_range_days: int
|
|
observable_member_count: int
|
|
total_member_count: int
|
|
team_totals: Dict[str, Any] = Field(
|
|
default_factory=dict,
|
|
description="Aggregated metrics: conversations, messages, tokens"
|
|
)
|
|
member_breakdown: List[Dict[str, Any]] = Field(
|
|
default_factory=list,
|
|
description="Per-member activity stats"
|
|
)
|
|
time_series: List[Dict[str, Any]] = Field(
|
|
default_factory=list,
|
|
description="Activity over time"
|
|
)
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class TeamActivityResponse(BaseModel):
|
|
"""Response model for team activity"""
|
|
data: TeamActivityMetrics
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# Error Response Models
|
|
|
|
class ErrorDetail(BaseModel):
|
|
"""Error detail model"""
|
|
message: str
|
|
field: Optional[str] = None
|
|
code: Optional[str] = None
|
|
|
|
|
|
class ErrorResponse(BaseModel):
|
|
"""Error response model"""
|
|
error: str
|
|
details: Optional[List[ErrorDetail]] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|