from pydantic import BaseModel, EmailStr, Field from typing import Optional, List class UsageTimelineEntry(BaseModel): date: str = Field(..., description="Date in YYYY-MM-DD format") message_count: int = Field(..., ge=0, description="Number of messages on this date") token_count: int = Field(0, ge=0, description="Number of tokens consumed on this date") # Closed-list values for the new governance/registration fields. Kept as module-level # constants so the form, API, and migrations all reference the same source of truth. BUSINESS_ENTITIES = ["OLIVER", "DARE", "Brandtech Group", "Pencil", "Jellyfish", "Adjust", "Other"] CLIENT_SCOPES = ["internal", "all", "specific"] AGENT_CLASSIFICATIONS = ["Utility", "Functional", "Supervisory", "Guardian"] AUTONOMY_LEVELS = ["Human-Led", "Hybrid", "Autopilot"] IP_OWNERSHIPS = ["Brandtech IP", "Client IP", "Shared/TBD"] DISCIPLINES = [ "Strategy", "Creative", "Oversight including delivery", "Optimisation", "Back Office including operations", "Pencil Agents", ] class AgentSafety(BaseModel): off_switch_confirmed: Optional[bool] = None access_rights_confirmed: Optional[bool] = None class AgentPII(BaseModel): handles_pii: Optional[bool] = None legal_ref: Optional[str] = None data_types: Optional[str] = None consent_recorded: Optional[bool] = None class AgentDeclarations(BaseModel): governance: Optional[bool] = None accuracy: Optional[bool] = None upkeep: Optional[bool] = None class AiAgent(BaseModel): agent_id: int agent_name: str agent_tool: str | None = Field(default=None, title="The tool or platform where the agent operates", max_length=100) agent_description: str | None = Field(default=None, title="The description of the agent", max_length=300) agent_purpose: str | None = Field(default=None, title="The purpose of the agent", max_length=200) agent_version: str | None = Field(default=None, title="The version of the agent", max_length=100) agent_status: str | None = Field(default=None, title="The status of the agent", max_length=100, enum=['Active', 'Inactive', 'Deprecated', 'Development']) agent_location: str | None = Field(default=None, title="The location of the agent", max_length=100) agent_department: str | None = Field(default=None, title="The department of the agent", max_length=100) agent_contact_person: str | None = Field(default=None, title="The contact person for the agent", max_length=100) agent_created_at: str | None = Field(default=None, title="The creation date of the agent", max_length=100) agent_updated_at: str | None = Field(default=None, title="The last update date of the agent", max_length=100) agent_tags: list[str] | None = Field(default=None, title="Tags associated with the agent", max_length=100) agent_metadata: dict[str, str] | None = Field(default=None, title="Metadata associated with the agent") agent_userbase: list[str] | None = Field(default=None, title="Userbase associated with the agent") agent_capabilities: list[str] | None = Field(default=None, title="Capabilities of the agent") url: str | None = Field(default=None, title="Direct link to create a conversation with this agent") quality_audit_status: bool | None = Field(default=False, title="Quality audit status") quality_audit_updated_by: str | None = Field(default=None, title="Admin user ID who updated quality audit") quality_audit_updated_at: str | None = Field(default=None, title="Quality audit last update timestamp") quality_audit_updated_by_name: str | None = Field(default=None, title="Admin user name who updated quality audit") risk_factor: int | None = Field(default=None, title="Risk factor rating (1-5)", ge=1, le=5) last_edited_by: str | None = Field(default=None, title="Email of user who last edited this agent") discipline: str | None = Field(default=None, title="Business discipline/category", max_length=100) rating: float | None = Field(default=None, title="Star rating (1-5)", ge=1, le=5) instructions: str | None = Field(default=None, title="System prompt / instructions from LibreChat") # Governance / registration fields (introduced 2026-05) business_entity: Optional[str] = Field(default=None, title="Group company the agent sits under") client_scope: Optional[str] = Field(default=None, title="internal / all / specific") agent_classification: Optional[str] = Field(default=None, title="Utility / Functional / Supervisory / Guardian") autonomy_level: Optional[str] = Field(default=None, title="Human-Led / Hybrid / Autopilot") ip_ownership: Optional[str] = Field(default=None, title="Brandtech IP / Client IP / Shared/TBD") foundation_model: Optional[str] = Field(default=None, title="Primary LLM provider/model") safety: Optional[AgentSafety] = None pii: Optional[AgentPII] = None validated_by: Optional[str] = None validation_date: Optional[str] = None evals_method: Optional[str] = None declarations: Optional[AgentDeclarations] = None registration_complete: Optional[bool] = Field(default=None, title="Whether all required registration fields are populated") # User Base Model class UserCreate(BaseModel): email: EmailStr password: str full_name: Optional[str] = None class UserLogin(BaseModel): email: EmailStr password: str class UserResponse(BaseModel): email: EmailStr full_name: Optional[str] = None is_active: bool is_admin: bool role: Optional[str] = "user" auth_provider: Optional[str] = "local" class UserUpdate(BaseModel): full_name: Optional[str] = None is_active: Optional[bool] = None is_admin: Optional[bool] = None role: Optional[str] = Field(default=None, pattern="^(user|admin|readonly_admin)$") class Token(BaseModel): access_token: str token_type: str = "bearer" # Admin user management models class AdminUserCreate(BaseModel): email: EmailStr full_name: Optional[str] = None password: str = Field(..., min_length=8) is_admin: bool = False class AdminPasswordReset(BaseModel): new_password: str = Field(..., min_length=8) class PasswordChange(BaseModel): current_password: str new_password: str = Field(..., min_length=8) # Agent models for creation and response class AiAgentCreate(BaseModel): agent_name: str agent_tool: str agent_description: Optional[str] = None agent_purpose: Optional[str] = None agent_version: Optional[str] = None agent_status: Optional[str] = "Development" agent_location: Optional[str] = None agent_department: Optional[str] = None agent_contact_person: Optional[str] = None agent_tags: Optional[list[str]] = None agent_metadata: Optional[dict[str, str]] = None agent_userbase: Optional[list[str]] = None agent_capabilities: Optional[list[str]] = None url: Optional[str] = None quality_audit_status: Optional[bool] = False quality_audit_updated_by: Optional[str] = None quality_audit_updated_at: Optional[str] = None quality_audit_updated_by_name: Optional[str] = None risk_factor: Optional[int] = Field(default=None, ge=1, le=5) last_edited_by: Optional[str] = None discipline: Optional[str] = None rating: Optional[float] = Field(default=None, ge=1, le=5) client: Optional[str] = None client_name: Optional[str] = None studio_name: Optional[str] = None instructions: Optional[str] = None # Governance / registration fields (introduced 2026-05) business_entity: Optional[str] = None client_scope: Optional[str] = Field(default=None, pattern="^(internal|all|specific)$") agent_classification: Optional[str] = Field(default=None, pattern="^(Utility|Functional|Supervisory|Guardian)$") autonomy_level: Optional[str] = Field(default=None, pattern="^(Human-Led|Hybrid|Autopilot)$") ip_ownership: Optional[str] = None foundation_model: Optional[str] = None safety: Optional[AgentSafety] = None pii: Optional[AgentPII] = None validated_by: Optional[str] = None validation_date: Optional[str] = None evals_method: Optional[str] = None declarations: Optional[AgentDeclarations] = None registration_complete: Optional[bool] = None class AiAgentResponse(BaseModel): agent_id: str agent_name: str agent_tool: Optional[str] = None agent_description: Optional[str] = None agent_purpose: Optional[str] = None agent_version: Optional[str] = None agent_status: Optional[str] = None agent_location: Optional[str] = None agent_department: Optional[str] = None agent_contact_person: Optional[str] = None agent_created_at: Optional[str] = None agent_updated_at: Optional[str] = None agent_tags: Optional[list[str]] = None agent_metadata: Optional[dict[str, str]] = None agent_userbase: Optional[list[str]] = None agent_capabilities: Optional[list[str]] = None url: Optional[str] = None quality_audit_status: Optional[bool] = None quality_audit_updated_by: Optional[str] = None quality_audit_updated_at: Optional[str] = None quality_audit_updated_by_name: Optional[str] = None risk_factor: Optional[int] = None last_edited_by: Optional[str] = None discipline: Optional[str] = None rating: Optional[float] = None rating_count: Optional[int] = None client: Optional[str] = None client_name: Optional[str] = None studio_name: Optional[str] = None verification_status: Optional[str] = None verified_by: Optional[str] = None verified_date: Optional[str] = None instructions: Optional[str] = None audit_status: Optional[str] = None audit_date: Optional[str] = None audit_category: Optional[str] = None audit_risk_level: Optional[str] = None created_by: str # Usage tracking fields (new) usage_timeline: Optional[List[dict]] = None conversation_count: Optional[int] = None unique_users: Optional[int] = None total_messages: Optional[int] = None first_used: Optional[str] = None last_used: Optional[str] = None total_tokens: Optional[int] = None prompt_tokens: Optional[int] = None completion_tokens: Optional[int] = None # Governance / registration fields (introduced 2026-05) business_entity: Optional[str] = None client_scope: Optional[str] = None agent_classification: Optional[str] = None autonomy_level: Optional[str] = None ip_ownership: Optional[str] = None foundation_model: Optional[str] = None safety: Optional[AgentSafety] = None pii: Optional[AgentPII] = None validated_by: Optional[str] = None validation_date: Optional[str] = None evals_method: Optional[str] = None declarations: Optional[AgentDeclarations] = None registration_complete: Optional[bool] = None # Agent Collector API Models (for compatibility with agent_collector app) class AgentCollectorCreate(BaseModel): name: str = Field(min_length=1) description: str = Field(min_length=1) purpose: str = Field(min_length=1) tool: str = Field(min_length=1) location: Optional[str] = None userbase: Optional[list[str]] = None version: Optional[str] = None creation_date: Optional[str] = None # ISO 8601 datetime string last_updated: Optional[str] = None # ISO 8601 datetime string capabilities: Optional[list[str]] = None status: Optional[str] = Field(default="development", pattern="^(?i)(active|inactive|deprecated|development)$") department: Optional[str] = None contact_person: Optional[str] = None tags: Optional[list[str]] = None metadata: Optional[dict] = None url: Optional[str] = None discipline: Optional[str] = None client: Optional[str] = None client_name: Optional[str] = None studio_name: Optional[str] = None instructions: Optional[str] = None # Governance / registration fields — accepted optionally so the collector # can opt into sending them later without an API change. business_entity: Optional[str] = None client_scope: Optional[str] = None agent_classification: Optional[str] = None autonomy_level: Optional[str] = None ip_ownership: Optional[str] = None foundation_model: Optional[str] = None safety: Optional[AgentSafety] = None pii: Optional[AgentPII] = None validated_by: Optional[str] = None validation_date: Optional[str] = None evals_method: Optional[str] = None declarations: Optional[AgentDeclarations] = None # Usage tracking fields (new) usage_timeline: Optional[List[UsageTimelineEntry]] = None conversation_count: Optional[int] = Field(default=None, ge=0) unique_users: Optional[int] = Field(default=None, ge=0) total_messages: Optional[int] = Field(default=None, ge=0) first_used: Optional[str] = None # ISO 8601 datetime string last_used: Optional[str] = None # ISO 8601 datetime string total_tokens: Optional[int] = Field(default=None, ge=0) prompt_tokens: Optional[int] = Field(default=None, ge=0) completion_tokens: Optional[int] = Field(default=None, ge=0) class AgentCollectorResponse(BaseModel): status: str = "success" message: str = "Agent data collected successfully" agent_id: str class HealthCheckResponse(BaseModel): status: str message: str timestamp: str database: dict class AgentUsageTrackingResponse(BaseModel): status: str = "usage_logged" message: str = "Agent already exists, usage tracked" agent_name: str class AgentUsageRecord(BaseModel): agent_name: str agent_data: dict timestamp: str usage_count: Optional[int] = None class AuditReviewRequest(BaseModel): audit_status: str = Field(..., pattern="^(flagged|reviewed|cleared)$") reviewer_notes: Optional[str] = None class AgentUsageStatsResponse(BaseModel): agent_name: str total_usage_count: int first_usage: Optional[str] = None last_usage: Optional[str] = None usage_by_period: dict conversation_count: Optional[int] = None unique_users: Optional[int] = None total_tokens: Optional[int] = None prompt_tokens: Optional[int] = None completion_tokens: Optional[int] = None