chore: ran frontend and backend linting

This commit is contained in:
Anish Sarkar 2026-02-01 22:54:25 +05:30
parent ff4a574248
commit 085653d3e3
32 changed files with 288 additions and 210 deletions

View file

@ -10,10 +10,6 @@ from collections.abc import Awaitable, Callable
from datetime import UTC, datetime from datetime import UTC, datetime
from typing import Any from typing import Any
# Heartbeat configuration
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
HEARTBEAT_INTERVAL_SECONDS = 30
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select from sqlalchemy.future import select
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
@ -32,6 +28,10 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Heartbeat configuration
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
HEARTBEAT_INTERVAL_SECONDS = 30
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -10,10 +10,6 @@ from collections.abc import Awaitable, Callable
from datetime import UTC, datetime from datetime import UTC, datetime
from typing import Any from typing import Any
# Heartbeat configuration
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
HEARTBEAT_INTERVAL_SECONDS = 30
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select from sqlalchemy.future import select
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
@ -35,6 +31,10 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Heartbeat configuration
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
HEARTBEAT_INTERVAL_SECONDS = 30
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -15,10 +15,6 @@ from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
# Heartbeat configuration
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
HEARTBEAT_INTERVAL_SECONDS = 30
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm.attributes import flag_modified from sqlalchemy.orm.attributes import flag_modified
@ -35,6 +31,10 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Heartbeat configuration
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
HEARTBEAT_INTERVAL_SECONDS = 30
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -61,7 +61,9 @@ class DiscordConnector(commands.Bot):
self.token = None self.token = None
self._bot_task = None # Holds the async bot task self._bot_task = None # Holds the async bot task
self._is_running = False # Flag to track if the bot is running self._is_running = False # Flag to track if the bot is running
self._start_called_event = asyncio.Event() # Event to signal when start() is called self._start_called_event = (
asyncio.Event()
) # Event to signal when start() is called
# Event to confirm bot is ready # Event to confirm bot is ready
@self.event @self.event
@ -293,7 +295,7 @@ class DiscordConnector(commands.Bot):
logger.error("start_bot() did not call start() within 30 seconds") logger.error("start_bot() did not call start() within 30 seconds")
raise RuntimeError( raise RuntimeError(
"Discord client failed to initialize - start() was never called" "Discord client failed to initialize - start() was never called"
) ) from None
try: try:
await asyncio.wait_for(self.wait_until_ready(), timeout=60.0) await asyncio.wait_for(self.wait_until_ready(), timeout=60.0)

View file

@ -252,12 +252,16 @@ class GoogleCalendarConnector:
if dt_start.tzinfo is None: if dt_start.tzinfo is None:
dt_start = dt_start.replace(hour=0, minute=0, second=0, tzinfo=pytz.UTC) dt_start = dt_start.replace(hour=0, minute=0, second=0, tzinfo=pytz.UTC)
else: else:
dt_start = dt_start.astimezone(pytz.UTC).replace(hour=0, minute=0, second=0) dt_start = dt_start.astimezone(pytz.UTC).replace(
hour=0, minute=0, second=0
)
if dt_end.tzinfo is None: if dt_end.tzinfo is None:
dt_end = dt_end.replace(hour=23, minute=59, second=59, tzinfo=pytz.UTC) dt_end = dt_end.replace(hour=23, minute=59, second=59, tzinfo=pytz.UTC)
else: else:
dt_end = dt_end.astimezone(pytz.UTC).replace(hour=23, minute=59, second=59) dt_end = dt_end.astimezone(pytz.UTC).replace(
hour=23, minute=59, second=59
)
if dt_start >= dt_end: if dt_start >= dt_end:
return [], ( return [], (

View file

@ -46,6 +46,11 @@ SCOPES = [
"guilds.members.read", # Read member information "guilds.members.read", # Read member information
] ]
# Discord permission bits
VIEW_CHANNEL = 1 << 10 # 1024
READ_MESSAGE_HISTORY = 1 << 16 # 65536
ADMINISTRATOR = 1 << 3 # 8
# Initialize security utilities # Initialize security utilities
_state_manager = None _state_manager = None
_token_encryption = None _token_encryption = None
@ -582,10 +587,10 @@ def _compute_channel_permissions(
elif overwrite_id in bot_role_ids: elif overwrite_id in bot_role_ids:
role_allow |= allow role_allow |= allow
role_deny |= deny role_deny |= deny
elif overwrite_type == 1: # Member overwrite elif overwrite_type == 1 and bot_user_id and overwrite_id == bot_user_id:
if bot_user_id and overwrite_id == bot_user_id: # Member-specific overwrite for the bot
member_allow = allow member_allow = allow
member_deny = deny member_deny = deny
# Apply in order per Discord docs: # Apply in order per Discord docs:
# 1. @everyone deny, then allow # 1. @everyone deny, then allow
@ -623,18 +628,14 @@ async def get_discord_channels(
""" """
from sqlalchemy import select from sqlalchemy import select
# Discord permission bits
VIEW_CHANNEL = 1 << 10 # 1024
READ_MESSAGE_HISTORY = 1 << 16 # 65536
ADMINISTRATOR = 1 << 3 # 8
try: try:
# Get connector and verify ownership # Get connector and verify ownership
result = await session.execute( result = await session.execute(
select(SearchSourceConnector).where( select(SearchSourceConnector).where(
SearchSourceConnector.id == connector_id, SearchSourceConnector.id == connector_id,
SearchSourceConnector.user_id == user.id, SearchSourceConnector.user_id == user.id,
SearchSourceConnector.connector_type == SearchSourceConnectorType.DISCORD_CONNECTOR, SearchSourceConnector.connector_type
== SearchSourceConnectorType.DISCORD_CONNECTOR,
) )
) )
connector = result.scalar_one_or_none() connector = result.scalar_one_or_none()
@ -685,7 +686,9 @@ async def get_discord_channels(
) )
if bot_user_response.status_code != 200: if bot_user_response.status_code != 200:
logger.warning(f"Failed to fetch bot user info: {bot_user_response.text}") logger.warning(
f"Failed to fetch bot user info: {bot_user_response.text}"
)
bot_user_id = None bot_user_id = None
else: else:
bot_user_id = bot_user_response.json().get("id") bot_user_id = bot_user_response.json().get("id")
@ -714,9 +717,13 @@ async def get_discord_channels(
) )
if bot_member_response.status_code != 200: if bot_member_response.status_code != 200:
logger.warning(f"Failed to fetch bot member info: {bot_member_response.text}") logger.warning(
f"Failed to fetch bot member info: {bot_member_response.text}"
)
bot_role_ids = {guild_id} # At minimum, bot has @everyone role bot_role_ids = {guild_id} # At minimum, bot has @everyone role
base_permissions = int(guild_roles.get(guild_id, {}).get("permissions", 0)) base_permissions = int(
guild_roles.get(guild_id, {}).get("permissions", 0)
)
else: else:
bot_member_data = bot_member_response.json() bot_member_data = bot_member_response.json()
bot_role_ids = set(bot_member_data.get("roles", [])) bot_role_ids = set(bot_member_data.get("roles", []))
@ -787,17 +794,21 @@ async def get_discord_channels(
# Bot can index if it has both VIEW_CHANNEL and READ_MESSAGE_HISTORY # Bot can index if it has both VIEW_CHANNEL and READ_MESSAGE_HISTORY
has_view = (effective_perms & VIEW_CHANNEL) == VIEW_CHANNEL has_view = (effective_perms & VIEW_CHANNEL) == VIEW_CHANNEL
has_read_history = (effective_perms & READ_MESSAGE_HISTORY) == READ_MESSAGE_HISTORY has_read_history = (
effective_perms & READ_MESSAGE_HISTORY
) == READ_MESSAGE_HISTORY
can_index = has_view and has_read_history can_index = has_view and has_read_history
text_channels.append({ text_channels.append(
"id": ch["id"], {
"name": ch["name"], "id": ch["id"],
"type": "text" if ch["type"] == 0 else "announcement", "name": ch["name"],
"position": ch.get("position", 0), "type": "text" if ch["type"] == 0 else "announcement",
"category_id": ch.get("parent_id"), "position": ch.get("position", 0),
"can_index": can_index, "category_id": ch.get("parent_id"),
}) "can_index": can_index,
}
)
# Sort by position # Sort by position
text_channels.sort(key=lambda x: x["position"]) text_channels.sort(key=lambda x: x["position"])

View file

@ -1168,9 +1168,10 @@ async def _run_indexing_with_notifications(
supports_retry_callback: Whether the indexing function supports on_retry_callback supports_retry_callback: Whether the indexing function supports on_retry_callback
supports_heartbeat_callback: Whether the indexing function supports on_heartbeat_callback supports_heartbeat_callback: Whether the indexing function supports on_heartbeat_callback
""" """
from celery.exceptions import SoftTimeLimitExceeded
from uuid import UUID from uuid import UUID
from celery.exceptions import SoftTimeLimitExceeded
notification = None notification = None
# Track indexed count for retry notifications and heartbeat # Track indexed count for retry notifications and heartbeat
current_indexed_count = 0 current_indexed_count = 0
@ -1241,11 +1242,13 @@ async def _run_indexing_with_notifications(
if notification: if notification:
try: try:
await session.refresh(notification) await session.refresh(notification)
await NotificationService.connector_indexing.notify_indexing_progress( await (
session=session, NotificationService.connector_indexing.notify_indexing_progress(
notification=notification, session=session,
indexed_count=indexed_count, notification=notification,
stage="processing", indexed_count=indexed_count,
stage="processing",
)
) )
await session.commit() await session.commit()
except Exception as e: except Exception as e:
@ -1447,7 +1450,9 @@ async def _run_indexing_with_notifications(
) )
await session.commit() await session.commit()
except Exception as notif_error: except Exception as notif_error:
logger.error(f"Failed to update notification on soft timeout: {notif_error!s}") logger.error(
f"Failed to update notification on soft timeout: {notif_error!s}"
)
# Re-raise so Celery knows the task was terminated # Re-raise so Celery knows the task was terminated
raise raise

View file

@ -547,7 +547,8 @@ async def get_slack_channels(
select(SearchSourceConnector).where( select(SearchSourceConnector).where(
SearchSourceConnector.id == connector_id, SearchSourceConnector.id == connector_id,
SearchSourceConnector.user_id == user.id, SearchSourceConnector.user_id == user.id,
SearchSourceConnector.connector_type == SearchSourceConnectorType.SLACK_CONNECTOR, SearchSourceConnector.connector_type
== SearchSourceConnectorType.SLACK_CONNECTOR,
) )
) )
connector = result.scalar_one_or_none() connector = result.scalar_one_or_none()

View file

@ -9,8 +9,7 @@ frontend from showing a perpetual "syncing" state.
import logging import logging
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
from sqlalchemy import and_, update from sqlalchemy import and_
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from sqlalchemy.future import select from sqlalchemy.future import select
from sqlalchemy.orm.attributes import flag_modified from sqlalchemy.orm.attributes import flag_modified
@ -78,7 +77,8 @@ async def _cleanup_stale_notifications():
select(Notification).filter( select(Notification).filter(
and_( and_(
Notification.type == "connector_indexing", Notification.type == "connector_indexing",
Notification.notification_metadata["status"].astext == "in_progress", Notification.notification_metadata["status"].astext
== "in_progress",
Notification.updated_at < cutoff_time, Notification.updated_at < cutoff_time,
) )
) )
@ -98,8 +98,12 @@ async def _cleanup_stale_notifications():
for notification in stale_notifications: for notification in stale_notifications:
try: try:
# Get current indexed count from metadata if available # Get current indexed count from metadata if available
indexed_count = notification.notification_metadata.get("indexed_count", 0) indexed_count = notification.notification_metadata.get(
connector_name = notification.notification_metadata.get("connector_name", "Unknown") "indexed_count", 0
)
connector_name = notification.notification_metadata.get(
"connector_name", "Unknown"
)
# Calculate how long it's been stale # Calculate how long it's been stale
stale_duration = datetime.now(UTC) - notification.updated_at stale_duration = datetime.now(UTC) - notification.updated_at
@ -107,7 +111,9 @@ async def _cleanup_stale_notifications():
# Update notification metadata # Update notification metadata
notification.notification_metadata["status"] = "failed" notification.notification_metadata["status"] = "failed"
notification.notification_metadata["completed_at"] = datetime.now(UTC).isoformat() notification.notification_metadata["completed_at"] = datetime.now(
UTC
).isoformat()
notification.notification_metadata["error_message"] = ( notification.notification_metadata["error_message"] = (
f"Indexing task appears to have crashed or timed out. " f"Indexing task appears to have crashed or timed out. "
f"No activity detected for {stale_minutes} minutes. " f"No activity detected for {stale_minutes} minutes. "
@ -138,4 +144,3 @@ async def _cleanup_stale_notifications():
except Exception as e: except Exception as e:
logger.error(f"Error cleaning up stale notifications: {e!s}", exc_info=True) logger.error(f"Error cleaning up stale notifications: {e!s}", exc_info=True)
await session.rollback() await session.rollback()

View file

@ -12,9 +12,6 @@ import logging
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from importlib import import_module from importlib import import_module
# Type alias for heartbeat callback function
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select from sqlalchemy.future import select
@ -26,6 +23,9 @@ from app.db import (
from app.services.composio_service import INDEXABLE_TOOLKITS, TOOLKIT_TO_INDEXER from app.services.composio_service import INDEXABLE_TOOLKITS, TOOLKIT_TO_INDEXER
from app.services.task_logging_service import TaskLoggingService from app.services.task_logging_service import TaskLoggingService
# Type alias for heartbeat callback function
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Set up logging # Set up logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -20,12 +20,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
calculate_date_range, calculate_date_range,
check_document_by_unique_identifier, check_document_by_unique_identifier,
@ -36,6 +30,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_airtable_records( async def index_airtable_records(
session: AsyncSession, session: AsyncSession,
@ -145,7 +144,11 @@ async def index_airtable_records(
# Process each base # Process each base
for base in bases: for base in bases:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time)
>= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(total_documents_indexed) await on_heartbeat_callback(total_documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
base_id = base.get("id") base_id = base.get("id")
@ -224,7 +227,11 @@ async def index_airtable_records(
# Process each record # Process each record
for record in records: for record in records:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time)
>= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(total_documents_indexed) await on_heartbeat_callback(total_documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()

View file

@ -21,12 +21,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
calculate_date_range, calculate_date_range,
check_document_by_unique_identifier, check_document_by_unique_identifier,
@ -37,6 +31,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_bookstack_pages( async def index_bookstack_pages(
session: AsyncSession, session: AsyncSession,
@ -194,7 +193,10 @@ async def index_bookstack_pages(
for page in pages: for page in pages:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
try: try:

View file

@ -22,12 +22,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
check_document_by_unique_identifier, check_document_by_unique_identifier,
check_duplicate_document_by_hash, check_duplicate_document_by_hash,
@ -37,6 +31,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_clickup_tasks( async def index_clickup_tasks(
session: AsyncSession, session: AsyncSession,
@ -184,7 +183,11 @@ async def index_clickup_tasks(
for task in tasks: for task in tasks:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time)
>= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()

View file

@ -22,12 +22,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
calculate_date_range, calculate_date_range,
check_document_by_unique_identifier, check_document_by_unique_identifier,
@ -38,6 +32,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_confluence_pages( async def index_confluence_pages(
session: AsyncSession, session: AsyncSession,
@ -190,7 +189,10 @@ async def index_confluence_pages(
for page in pages: for page in pages:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
try: try:

View file

@ -305,7 +305,11 @@ async def index_discord_messages(
try: try:
for guild in guilds: for guild in guilds:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time)
>= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
guild_id = guild["id"] guild_id = guild["id"]

View file

@ -21,18 +21,18 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
check_document_by_unique_identifier, check_document_by_unique_identifier,
check_duplicate_document_by_hash, check_duplicate_document_by_hash,
get_current_timestamp, get_current_timestamp,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -186,7 +186,11 @@ async def index_elasticsearch_documents(
fields=config.get("ELASTICSEARCH_FIELDS"), fields=config.get("ELASTICSEARCH_FIELDS"),
): ):
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time)
>= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_processed) await on_heartbeat_callback(documents_processed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()

View file

@ -24,12 +24,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
check_document_by_unique_identifier, check_document_by_unique_identifier,
check_duplicate_document_by_hash, check_duplicate_document_by_hash,
@ -38,6 +32,12 @@ from .base import (
logger, logger,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
# Maximum tokens for a single digest before splitting # Maximum tokens for a single digest before splitting
# Most LLMs can handle 128k+ tokens now, but we'll be conservative # Most LLMs can handle 128k+ tokens now, but we'll be conservative
MAX_DIGEST_CHARS = 500_000 # ~125k tokens MAX_DIGEST_CHARS = 500_000 # ~125k tokens
@ -184,7 +184,10 @@ async def index_github_repos(
for repo_full_name in repo_full_names_to_index: for repo_full_name in repo_full_names_to_index:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
if not repo_full_name or not isinstance(repo_full_name, str): if not repo_full_name or not isinstance(repo_full_name, str):

View file

@ -6,8 +6,6 @@ import time
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from datetime import datetime, timedelta from datetime import datetime, timedelta
import pytz
from dateutil.parser import isoparse
from google.oauth2.credentials import Credentials from google.oauth2.credentials import Credentials
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@ -23,12 +21,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
check_document_by_unique_identifier, check_document_by_unique_identifier,
check_duplicate_document_by_hash, check_duplicate_document_by_hash,
@ -38,6 +30,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_google_calendar_events( async def index_google_calendar_events(
session: AsyncSession, session: AsyncSession,
@ -296,7 +293,10 @@ async def index_google_calendar_events(
for event in events: for event in events:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
try: try:

View file

@ -420,7 +420,10 @@ async def _index_full_scan(
while folders_to_process and files_processed < max_files: while folders_to_process and files_processed < max_files:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
current_folder_id, current_folder_name = folders_to_process.pop(0) current_folder_id, current_folder_name = folders_to_process.pop(0)
@ -541,7 +544,10 @@ async def _index_with_delta_sync(
for change in changes: for change in changes:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
if files_processed >= max_files: if files_processed >= max_files:

View file

@ -25,12 +25,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
calculate_date_range, calculate_date_range,
check_document_by_unique_identifier, check_document_by_unique_identifier,
@ -41,6 +35,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_google_gmail_messages( async def index_google_gmail_messages(
session: AsyncSession, session: AsyncSession,
@ -228,7 +227,10 @@ async def index_google_gmail_messages(
for message in messages: for message in messages:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
try: try:

View file

@ -22,12 +22,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
calculate_date_range, calculate_date_range,
check_document_by_unique_identifier, check_document_by_unique_identifier,
@ -38,6 +32,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_jira_issues( async def index_jira_issues(
session: AsyncSession, session: AsyncSession,
@ -184,7 +183,10 @@ async def index_jira_issues(
for issue in issues: for issue in issues:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
try: try:

View file

@ -21,12 +21,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
calculate_date_range, calculate_date_range,
check_document_by_unique_identifier, check_document_by_unique_identifier,
@ -37,6 +31,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_linear_issues( async def index_linear_issues(
session: AsyncSession, session: AsyncSession,
@ -210,7 +209,10 @@ async def index_linear_issues(
# Process each issue # Process each issue
for issue in issues: for issue in issues:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()

View file

@ -21,12 +21,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
check_document_by_unique_identifier, check_document_by_unique_identifier,
check_duplicate_document_by_hash, check_duplicate_document_by_hash,
@ -36,6 +30,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_luma_events( async def index_luma_events(
session: AsyncSession, session: AsyncSession,
@ -236,7 +235,10 @@ async def index_luma_events(
for event in events: for event in events:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
try: try:

View file

@ -234,7 +234,10 @@ async def index_notion_pages(
# Process each page # Process each page
for page in pages: for page in pages:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()

View file

@ -27,12 +27,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
build_document_metadata_string, build_document_metadata_string,
check_document_by_unique_identifier, check_document_by_unique_identifier,
@ -43,6 +37,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
def parse_frontmatter(content: str) -> tuple[dict | None, str]: def parse_frontmatter(content: str) -> tuple[dict | None, str]:
""" """
@ -320,7 +319,10 @@ async def index_obsidian_vault(
for file_info in files: for file_info in files:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(indexed_count) await on_heartbeat_callback(indexed_count)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
try: try:

View file

@ -20,12 +20,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
build_document_metadata_markdown, build_document_metadata_markdown,
calculate_date_range, calculate_date_range,
@ -37,6 +31,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_slack_messages( async def index_slack_messages(
session: AsyncSession, session: AsyncSession,
@ -187,7 +186,10 @@ async def index_slack_messages(
# Process each channel # Process each channel
for channel_obj in channels: for channel_obj in channels:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
channel_id = channel_obj["id"] channel_id = channel_obj["id"]

View file

@ -19,12 +19,6 @@ from app.utils.document_converters import (
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
build_document_metadata_markdown, build_document_metadata_markdown,
calculate_date_range, calculate_date_range,
@ -36,6 +30,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds - update notification every 30 seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_teams_messages( async def index_teams_messages(
session: AsyncSession, session: AsyncSession,
@ -200,7 +199,10 @@ async def index_teams_messages(
# Process each team # Process each team
for team in teams: for team in teams:
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()

View file

@ -22,12 +22,6 @@ from app.utils.document_converters import (
) )
from app.utils.webcrawler_utils import parse_webcrawler_urls from app.utils.webcrawler_utils import parse_webcrawler_urls
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
from .base import ( from .base import (
check_document_by_unique_identifier, check_document_by_unique_identifier,
check_duplicate_document_by_hash, check_duplicate_document_by_hash,
@ -37,6 +31,11 @@ from .base import (
update_connector_last_indexed, update_connector_last_indexed,
) )
# Type hint for heartbeat callback
HeartbeatCallbackType = Callable[[int], Awaitable[None]]
# Heartbeat interval in seconds
HEARTBEAT_INTERVAL_SECONDS = 30
async def index_crawled_urls( async def index_crawled_urls(
session: AsyncSession, session: AsyncSession,
@ -155,7 +154,10 @@ async def index_crawled_urls(
for idx, url in enumerate(urls, 1): for idx, url in enumerate(urls, 1):
# Check if it's time for a heartbeat update # Check if it's time for a heartbeat update
if on_heartbeat_callback and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS: if (
on_heartbeat_callback
and (time.time() - last_heartbeat_time) >= HEARTBEAT_INTERVAL_SECONDS
):
await on_heartbeat_callback(documents_indexed) await on_heartbeat_callback(documents_indexed)
last_heartbeat_time = time.time() last_heartbeat_time = time.time()
try: try:

View file

@ -80,8 +80,8 @@ export const DiscordConfig: FC<DiscordConfigProps> = ({ connector }) => {
</div> </div>
<div className="text-xs sm:text-sm"> <div className="text-xs sm:text-sm">
<p className="text-muted-foreground mt-1 text-[10px] sm:text-sm"> <p className="text-muted-foreground mt-1 text-[10px] sm:text-sm">
The bot needs &quot;Read Message History&quot; permission to index channels. The bot needs &quot;Read Message History&quot; permission to index channels. Ask a
Ask a server admin to grant this permission for channels shown below. server admin to grant this permission for channels shown below.
</p> </p>
</div> </div>
</div> </div>
@ -122,7 +122,8 @@ export const DiscordConfig: FC<DiscordConfigProps> = ({ connector }) => {
</div> </div>
) : channels.length === 0 && !error ? ( ) : channels.length === 0 && !error ? (
<div className="text-center py-8 text-sm text-muted-foreground"> <div className="text-center py-8 text-sm text-muted-foreground">
No channels found. Make sure the bot has been added to your Discord server with proper permissions. No channels found. Make sure the bot has been added to your Discord server with proper
permissions.
</div> </div>
) : ( ) : (
<div className="rounded-xl bg-slate-400/5 dark:bg-white/5 overflow-hidden"> <div className="rounded-xl bg-slate-400/5 dark:bg-white/5 overflow-hidden">

View file

@ -136,8 +136,7 @@ export const SlackConfig: FC<SlackConfigProps> = ({ connector }) => {
<CheckCircle2 className="size-3.5 text-emerald-500" /> <CheckCircle2 className="size-3.5 text-emerald-500" />
<span className="text-[11px] font-medium">Ready to index</span> <span className="text-[11px] font-medium">Ready to index</span>
<span className="text-[10px] text-muted-foreground"> <span className="text-[10px] text-muted-foreground">
{channelsWithBot.length}{" "} {channelsWithBot.length} {channelsWithBot.length === 1 ? "channel" : "channels"}
{channelsWithBot.length === 1 ? "channel" : "channels"}
</span> </span>
</div> </div>
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">

View file

@ -30,7 +30,12 @@ export function SidebarSection({
<Collapsible <Collapsible
open={isOpen} open={isOpen}
onOpenChange={setIsOpen} onOpenChange={setIsOpen}
className={cn("overflow-hidden", fillHeight && "flex flex-col min-h-0", fillHeight && isOpen && "flex-1", className)} className={cn(
"overflow-hidden",
fillHeight && "flex flex-col min-h-0",
fillHeight && isOpen && "flex-1",
className
)}
> >
<div className="flex items-center group/section shrink-0"> <div className="flex items-center group/section shrink-0">
<CollapsibleTrigger className="flex flex-1 items-center gap-1.5 px-2 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors min-w-0"> <CollapsibleTrigger className="flex flex-1 items-center gap-1.5 px-2 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors min-w-0">
@ -56,15 +61,11 @@ export function SidebarSection({
)} )}
</div> </div>
<CollapsibleContent <CollapsibleContent className={cn("overflow-hidden flex-1 flex flex-col min-h-0")}>
className={cn("overflow-hidden flex-1 flex flex-col min-h-0")} <div className={cn("px-2 pb-2 flex-1 flex flex-col min-h-0 overflow-hidden")}>
> {children}
<div </div>
className={cn("px-2 pb-2 flex-1 flex flex-col min-h-0 overflow-hidden")} </CollapsibleContent>
>
{children}
</div>
</CollapsibleContent>
</Collapsible> </Collapsible>
); );
} }

View file

@ -22,4 +22,3 @@ export function formatRelativeDate(dateString: string): string {
if (daysAgo < 7) return `${daysAgo}d ago`; if (daysAgo < 7) return `${daysAgo}d ago`;
return format(date, "MMM d, yyyy"); return format(date, "MMM d, yyyy");
} }