platform-core/modules/datamodels/datamodelDataSource.py
2026-05-18 07:56:53 +02:00

127 lines
5.7 KiB
Python

# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""DataSource and ExternalEntry models for external data integration.
DataSource links a UserConnection to an external path (SharePoint folder,
Google Drive folder, FTP directory, etc.) for agent-accessible data containers.
"""
from typing import Dict, Any, Optional
from pydantic import BaseModel, Field
from modules.datamodels.datamodelBase import PowerOnModel
from modules.shared.i18nRegistry import i18nModel
import uuid
@i18nModel("Datenquelle")
class DataSource(PowerOnModel):
"""Konfigurierte externe Datenquelle verknuepft mit einer UserConnection."""
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
description="Primary key",
json_schema_extra={"label": "ID"},
)
connectionId: str = Field(
description="FK to UserConnection",
json_schema_extra={"label": "Verbindungs-ID", "fk_target": {"db": "poweron_app", "table": "UserConnection", "labelField": "externalUsername"}},
)
sourceType: str = Field(
description=(
"sharepointFolder, onedriveFolder, googleDriveFolder, "
"outlookFolder, gmailFolder, ftpFolder, clickupList "
"(path under /team/...), kdriveFolder, calendarFolder, "
"contactFolder"
),
json_schema_extra={"label": "Quellentyp"},
)
path: str = Field(
description="External path (e.g. '/sites/MySite/Documents/Reports')",
json_schema_extra={"label": "Pfad"},
)
label: str = Field(
description="User-visible label (often the last path segment)",
json_schema_extra={"label": "Bezeichnung"},
)
displayPath: Optional[str] = Field(
default=None,
description="Human-readable full path for UI (connection-relative, slash-separated)",
json_schema_extra={"label": "Anzeigepfad"},
)
featureInstanceId: Optional[str] = Field(
default=None,
description="Scoped to feature instance",
json_schema_extra={"label": "Feature-Instanz", "fk_target": {"db": "poweron_app", "table": "FeatureInstance", "labelField": "label"}},
)
mandateId: Optional[str] = Field(
default=None,
description="Mandate scope",
json_schema_extra={"label": "Mandanten-ID", "fk_target": {"db": "poweron_app", "table": "Mandate", "labelField": "label"}},
)
userId: str = Field(
default="",
description="Owner user ID",
json_schema_extra={"label": "Benutzer-ID", "fk_target": {"db": "poweron_app", "table": "UserInDB", "labelField": "username"}},
)
ragIndexEnabled: Optional[bool] = Field(
default=None,
description=(
"Three-state RAG indexing flag with cascade-inherit semantics. "
"None = inherit from nearest ancestor DataSource (path-traversal); "
"True/False = explicit override that propagates to descendants. "
"Walker computes effective value via getEffectiveFlag()."
),
json_schema_extra={"label": "Im RAG indexieren", "frontend_type": "checkbox", "frontend_readonly": False, "frontend_required": False},
)
lastIndexed: Optional[float] = Field(
default=None,
description="Timestamp of last successful RAG indexing run",
json_schema_extra={"label": "Letzte Indexierung", "frontend_type": "timestamp"},
)
scope: Optional[str] = Field(
default=None,
description=(
"Data visibility scope with inherit semantics. "
"None = inherit; values: personal, featureInstance, mandate, global. "
"Cascade-reset on parent toggle."
),
json_schema_extra={"label": "Sichtbarkeit", "frontend_type": "select", "frontend_readonly": False, "frontend_required": False, "frontend_options": [
{"value": "personal", "label": "Persönlich"},
{"value": "featureInstance", "label": "Feature-Instanz"},
{"value": "mandate", "label": "Mandant"},
{"value": "global", "label": "Global"},
]},
)
neutralize: Optional[bool] = Field(
default=None,
description=(
"Three-state neutralization flag with cascade-inherit semantics. "
"None = inherit from nearest ancestor DataSource (path-traversal); "
"True/False = explicit override that propagates to descendants."
),
json_schema_extra={"label": "Neutralisieren", "frontend_type": "checkbox", "frontend_readonly": False, "frontend_required": False},
)
settings: Optional[Dict[str, Any]] = Field(
default=None,
description=(
"DataSource-scoped settings (JSON). Currently used keys: "
"ragLimits.{maxBytes,maxFileSize,maxItems,maxDepth}. "
"Walker reads these directly; missing keys fall back to RAG_LIMITS_DEFAULT "
"and are lazily persisted on next bootstrap."
),
json_schema_extra={"label": "Einstellungen", "frontend_type": "json", "frontend_readonly": True, "frontend_required": False},
)
class ExternalEntry(BaseModel):
"""An item (file or folder) from an external data source."""
name: str = Field(description="Item name")
path: str = Field(description="Full path within the source")
isFolder: bool = Field(default=False, description="True if directory/folder")
size: Optional[int] = Field(default=None, description="File size in bytes")
mimeType: Optional[str] = Field(default=None, description="MIME type (files only)")
lastModified: Optional[float] = Field(
default=None,
description="Last modification timestamp",
json_schema_extra={"frontend_type": "timestamp"},
)
metadata: Dict[str, Any] = Field(default_factory=dict, description="Provider-specific metadata")