abbe7a18fc
- P0: hooks.py 3-tuple fix, trigger_dispatcher Contract, contacts/plugin unregister_actions_by_owner - P0: 5 test files — check_permission mocks removed, hardcoded DB credential → env var - P1: attachment_service DmsFile via Contract helper, restore_registry/history_hooks dedup - P1: mail/plugin restore unregister, mcp_client datetime.now(UTC), saved_views/filters patterns - P1: ProtectedRoute fail-closed, 13 test assertion fixes (bcrypt, DB-URLs, SECRET_KEYs) - P2: deprecated notifications → post_system_message (3 files), forgejo Base, report_generator lazy import - P2: webhooks permissions, deps.py/roles.py plugin perms removed, import_export default - P2: address/tags/entity_links patterns removed, worker.py Contract-Umgehungen fixed - P2: 28 frontend TODOs (hardcoded constants, deprecated notification API) - P3: dead code, duplicates, deprecated imports, private attr, __import__ inline - P3: 8 frontend TODOs (LucideIcons, inline styles, XSS, i18n) - ruff: 838 → 0 (612 auto-fix + 246 manual + 27 F821 regression fix) - F821: 30 → 0 (AutomationDefinition, DmsFile, user_id, Path, Any, String) - Contract-Umgehungen: 2 neue gefunden (worker.py:169, worker.py:280) und gefixt
298 lines
8.8 KiB
Python
298 lines
8.8 KiB
Python
"""Pydantic schemas for the AI Assistant plugin."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
# ─── Providers ───
|
|
|
|
class AIProviderCreate(BaseModel):
|
|
name: str = Field(..., min_length=1, max_length=100)
|
|
provider_type: str = Field(..., min_length=1, max_length=50)
|
|
api_key: str = Field(default="", max_length=2000)
|
|
base_url: str = Field(default="", max_length=500)
|
|
is_active: bool = True
|
|
is_default: bool = False
|
|
config: dict[str, Any] = Field(default_factory=dict)
|
|
# Compliance metadata (B-AIPROV-COMP)
|
|
region: str = Field(default="unknown", max_length=20)
|
|
hosting_type: str = Field(default="cloud", max_length=30)
|
|
dpa_status: str = Field(default="none", max_length=20)
|
|
retention_policy: str = Field(default="")
|
|
training_on_customer_data: bool = False
|
|
transfer_notice: str = Field(default="")
|
|
allowed_data_classes: list[str] = Field(default_factory=list)
|
|
|
|
|
|
class AIProviderUpdate(BaseModel):
|
|
name: str | None = Field(None, min_length=1, max_length=100)
|
|
provider_type: str | None = Field(None, min_length=1, max_length=50)
|
|
api_key: str | None = Field(None, max_length=2000)
|
|
base_url: str | None = Field(None, max_length=500)
|
|
is_active: bool | None = None
|
|
is_default: bool | None = None
|
|
config: dict[str, Any] | None = None
|
|
# Compliance metadata (B-AIPROV-COMP)
|
|
region: str | None = Field(None, max_length=20)
|
|
hosting_type: str | None = Field(None, max_length=30)
|
|
dpa_status: str | None = Field(None, max_length=20)
|
|
retention_policy: str | None = None
|
|
training_on_customer_data: bool | None = None
|
|
transfer_notice: str | None = None
|
|
allowed_data_classes: list[str] | None = None
|
|
|
|
|
|
class AIProviderResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: str
|
|
name: str
|
|
provider_type: str
|
|
api_key: str # masked in routes
|
|
base_url: str
|
|
is_active: bool
|
|
is_default: bool
|
|
config: dict[str, Any]
|
|
# Compliance metadata (B-AIPROV-COMP)
|
|
region: str = "unknown"
|
|
hosting_type: str = "cloud"
|
|
dpa_status: str = "none"
|
|
retention_policy: str = ""
|
|
training_on_customer_data: bool = False
|
|
transfer_notice: str = ""
|
|
allowed_data_classes: list[str] = Field(default_factory=list)
|
|
created_at: datetime | None = None
|
|
updated_at: datetime | None = None
|
|
|
|
|
|
# ─── Models ───
|
|
|
|
class AIModelCreate(BaseModel):
|
|
provider_id: str
|
|
model_id: str = Field(..., min_length=1, max_length=200)
|
|
display_name: str = Field(..., min_length=1, max_length=200)
|
|
context_window: int = Field(default=4096, ge=1)
|
|
supports_tools: bool = False
|
|
supports_streaming: bool = True
|
|
is_active: bool = True
|
|
config: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class AIModelUpdate(BaseModel):
|
|
model_id: str | None = Field(None, min_length=1, max_length=200)
|
|
display_name: str | None = Field(None, min_length=1, max_length=200)
|
|
context_window: int | None = Field(None, ge=1)
|
|
supports_tools: bool | None = None
|
|
supports_streaming: bool | None = None
|
|
is_active: bool | None = None
|
|
config: dict[str, Any] | None = None
|
|
|
|
|
|
class AIModelResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: str
|
|
provider_id: str
|
|
model_id: str
|
|
display_name: str
|
|
context_window: int
|
|
supports_tools: bool
|
|
supports_streaming: bool
|
|
is_active: bool
|
|
config: dict[str, Any]
|
|
|
|
|
|
# ─── Presets ───
|
|
|
|
class AIPresetCreate(BaseModel):
|
|
name: str = Field(..., min_length=1, max_length=100)
|
|
model_id: str = Field(..., min_length=1, max_length=200)
|
|
provider_id: str | None = None
|
|
temperature: float = Field(default=0.7, ge=0.0, le=2.0)
|
|
max_tokens: int = Field(default=2048, ge=1, le=128000)
|
|
top_p: float = Field(default=1.0, ge=0.0, le=1.0)
|
|
system_prompt: str = ""
|
|
config: dict[str, Any] = Field(default_factory=dict)
|
|
is_active: bool = True
|
|
|
|
|
|
class AIPresetUpdate(BaseModel):
|
|
name: str | None = Field(None, min_length=1, max_length=100)
|
|
model_id: str | None = Field(None, min_length=1, max_length=200)
|
|
provider_id: str | None = None
|
|
temperature: float | None = Field(None, ge=0.0, le=2.0)
|
|
max_tokens: int | None = Field(None, ge=1, le=128000)
|
|
top_p: float | None = Field(None, ge=0.0, le=1.0)
|
|
system_prompt: str | None = None
|
|
config: dict[str, Any] | None = None
|
|
is_active: bool | None = None
|
|
|
|
|
|
class AIPresetResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: str
|
|
name: str
|
|
model_id: str
|
|
provider_id: str | None = None
|
|
temperature: float
|
|
max_tokens: int
|
|
top_p: float
|
|
system_prompt: str
|
|
config: dict[str, Any]
|
|
is_active: bool
|
|
|
|
|
|
# ─── Agents ───
|
|
|
|
class AIAgentCreate(BaseModel):
|
|
name: str = Field(..., min_length=1, max_length=100)
|
|
description: str = ""
|
|
system_prompt: str = ""
|
|
preset_id: str | None = None
|
|
tool_ids: list[str] = Field(default_factory=list)
|
|
is_active: bool = True
|
|
config: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class AIAgentUpdate(BaseModel):
|
|
name: str | None = Field(None, min_length=1, max_length=100)
|
|
description: str | None = None
|
|
system_prompt: str | None = None
|
|
preset_id: str | None = None
|
|
tool_ids: list[str] | None = None
|
|
is_active: bool | None = None
|
|
config: dict[str, Any] | None = None
|
|
|
|
|
|
class AIAgentResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: str
|
|
name: str
|
|
description: str
|
|
system_prompt: str
|
|
preset_id: str | None = None
|
|
tool_ids: list[str]
|
|
is_default: bool
|
|
is_active: bool
|
|
config: dict[str, Any]
|
|
created_at: datetime | None = None
|
|
updated_at: datetime | None = None
|
|
|
|
|
|
# ─── Chat Sessions ───
|
|
|
|
class ChatSessionCreate(BaseModel):
|
|
title: str = Field(default="Neuer Chat", max_length=255)
|
|
agent_id: str | None = None
|
|
is_sidebar: bool = False
|
|
folder_id: str | None = None
|
|
|
|
|
|
class ChatSessionUpdate(BaseModel):
|
|
title: str | None = Field(None, max_length=255)
|
|
is_pinned: bool | None = None
|
|
agent_id: str | None = None
|
|
folder_id: str | None = None
|
|
sort_order: int | None = None
|
|
|
|
|
|
class ChatSessionResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: str
|
|
user_id: str
|
|
agent_id: str | None = None
|
|
title: str
|
|
is_pinned: bool
|
|
is_sidebar: bool
|
|
folder_id: str | None = None
|
|
sort_order: int = 0
|
|
created_at: datetime | None = None
|
|
updated_at: datetime | None = None
|
|
|
|
|
|
# ─── Chat Messages ───
|
|
|
|
class ChatMessageResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: str
|
|
session_id: str
|
|
role: str
|
|
content: str
|
|
tool_calls: list[dict[str, Any]] | None = None
|
|
tool_results: list[dict[str, Any]] | None = None
|
|
tokens: int
|
|
model_used: str
|
|
created_at: datetime | None = None
|
|
|
|
|
|
class ChatSendRequest(BaseModel):
|
|
content: str = Field(..., min_length=1)
|
|
agent_id: str | None = None # override session agent
|
|
|
|
|
|
# ─── Folders ───
|
|
|
|
class ChatFolderCreate(BaseModel):
|
|
name: str = Field(..., min_length=1, max_length=255)
|
|
parent_id: str | None = None
|
|
|
|
|
|
class ChatFolderUpdate(BaseModel):
|
|
name: str | None = Field(None, min_length=1, max_length=255)
|
|
parent_id: str | None = None
|
|
sort_order: int | None = None
|
|
|
|
|
|
class ChatFolderResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: str
|
|
name: str
|
|
parent_id: str | None = None
|
|
user_id: str
|
|
sort_order: int = 0
|
|
created_at: datetime | None = None
|
|
|
|
|
|
# ─── Attachments ───
|
|
|
|
class ChatAttachmentResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: str
|
|
message_id: str | None = None
|
|
session_id: str
|
|
filename: str
|
|
mime_type: str
|
|
size_bytes: int
|
|
|
|
|
|
# ─── Tools ───
|
|
|
|
class AIToolResponse(BaseModel):
|
|
name: str
|
|
description: str
|
|
parameters: dict[str, Any]
|
|
plugin_name: str
|
|
required_permission: str | None = None
|
|
category: str
|
|
|
|
|
|
# ─── External Agent API Schemas (Phase 5.9) ───
|
|
|
|
|
|
class ExternalAgentRequest(BaseModel):
|
|
"""Request to run an AI agent from an external system."""
|
|
|
|
message: str = Field(..., min_length=1, description="The message/input for the agent")
|
|
context: dict[str, Any] = Field(default_factory=dict, description="Optional context data")
|
|
stream: bool = Field(default=False, description="Whether to stream the response")
|
|
|
|
|
|
class ExternalAgentResponse(BaseModel):
|
|
"""Response from an external agent execution."""
|
|
|
|
response: str = Field(default="", description="The agent's response text")
|
|
agent_id: str = Field(default="", description="ID of the agent that responded")
|
|
session_id: str = Field(default="", description="Chat session ID")
|
|
tokens_used: int = Field(default=0, description="Approximate token count")
|