122 lines
5.5 KiB
Python
122 lines
5.5 KiB
Python
# Copyright (c) 2026 PowerOn AG
|
|
# 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 was removed (privacy, 2026-06). Personal sources must not be
|
|
# shared across scopes. Only Files (folder-files) retain scope.
|
|
# The DB column is kept as deprecated-nullable to avoid a migration;
|
|
# it is never read or written by UDB/ingest/knowledge anymore.
|
|
scope: Optional[str] = Field(
|
|
default=None,
|
|
description="DEPRECATED (2026-06, privacy). Always None. Use Files scope instead.",
|
|
json_schema_extra={"frontend_readonly": True, "frontend_hidden": True},
|
|
)
|
|
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")
|