SurfSense/surfsense_backend/app/connectors/composio_connector.py

101 lines
3.2 KiB
Python
Raw Normal View History

2026-01-21 22:57:58 -08:00
"""
Composio Connector Base Module.
2026-01-21 22:57:58 -08:00
Provides a base class for interacting with various services via Composio,
2026-01-21 22:57:58 -08:00
primarily used during indexing operations.
"""
import logging
from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from app.db import SearchSourceConnector
2026-01-23 05:28:18 +05:30
from app.services.composio_service import INDEXABLE_TOOLKITS, ComposioService
2026-01-21 22:57:58 -08:00
logger = logging.getLogger(__name__)
class ComposioConnector:
"""
Base Composio connector for data retrieval.
2026-01-21 22:57:58 -08:00
Wraps the ComposioService to provide toolkit-specific data access
for indexing operations. Subclasses implement toolkit-specific methods.
2026-01-21 22:57:58 -08:00
"""
def __init__(
self,
session: AsyncSession,
connector_id: int,
):
"""
Initialize the Composio connector.
Args:
session: Database session for updating connector.
connector_id: ID of the SearchSourceConnector.
"""
self._session = session
self._connector_id = connector_id
self._service: ComposioService | None = None
self._connector: SearchSourceConnector | None = None
self._config: dict[str, Any] | None = None
async def _load_connector(self) -> SearchSourceConnector:
"""Load connector from database."""
if self._connector is None:
result = await self._session.execute(
select(SearchSourceConnector).filter(
SearchSourceConnector.id == self._connector_id
)
)
self._connector = result.scalars().first()
if not self._connector:
raise ValueError(f"Connector {self._connector_id} not found")
self._config = self._connector.config or {}
return self._connector
async def _get_service(self) -> ComposioService:
"""Get or create the Composio service instance."""
if self._service is None:
self._service = ComposioService()
return self._service
async def get_config(self) -> dict[str, Any]:
"""Get the connector configuration."""
await self._load_connector()
return self._config or {}
async def get_toolkit_id(self) -> str:
"""Get the toolkit ID for this connector."""
config = await self.get_config()
return config.get("toolkit_id", "")
async def get_connected_account_id(self) -> str | None:
"""Get the Composio connected account ID."""
config = await self.get_config()
return config.get("composio_connected_account_id")
async def get_entity_id(self) -> str:
"""Get the Composio entity ID (user identifier)."""
await self._load_connector()
# Entity ID is constructed from the connector's user_id
return f"surfsense_{self._connector.user_id}"
async def is_indexable(self) -> bool:
"""Check if this connector's toolkit supports indexing."""
toolkit_id = await self.get_toolkit_id()
return toolkit_id in INDEXABLE_TOOLKITS
@property
def session(self) -> AsyncSession:
"""Get the database session."""
return self._session
2026-01-21 22:57:58 -08:00
@property
def connector_id(self) -> int:
"""Get the connector ID."""
return self._connector_id