chore: linting

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-01-15 00:05:53 -08:00
parent 3375aeb9bc
commit 7ae68455b3
20 changed files with 128 additions and 103 deletions

View file

@ -24,9 +24,7 @@ def enum_exists(enum_name: str) -> bool:
"""Check if an enum type exists in the database.""" """Check if an enum type exists in the database."""
conn = op.get_bind() conn = op.get_bind()
result = conn.execute( result = conn.execute(
sa.text( sa.text("SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"),
"SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"
),
{"enum_name": enum_name}, {"enum_name": enum_name},
) )
return result.scalar() return result.scalar()

View file

@ -22,9 +22,7 @@ def enum_exists(enum_name: str) -> bool:
"""Check if an enum type exists in the database.""" """Check if an enum type exists in the database."""
conn = op.get_bind() conn = op.get_bind()
result = conn.execute( result = conn.execute(
sa.text( sa.text("SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"),
"SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"
),
{"enum_name": enum_name}, {"enum_name": enum_name},
) )
return result.scalar() return result.scalar()

View file

@ -197,9 +197,7 @@ def enum_exists(enum_name: str) -> bool:
"""Check if an enum type exists in the database.""" """Check if an enum type exists in the database."""
conn = op.get_bind() conn = op.get_bind()
result = conn.execute( result = conn.execute(
sa.text( sa.text("SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"),
"SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"
),
{"enum_name": enum_name}, {"enum_name": enum_name},
) )
return result.scalar() return result.scalar()

View file

@ -5,13 +5,14 @@ Revises: 61
Create Date: 2026-01-09 15:19:51.827647 Create Date: 2026-01-09 15:19:51.827647
""" """
from collections.abc import Sequence from collections.abc import Sequence
from alembic import op from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '62' revision: str = "62"
down_revision: str | None = '61' down_revision: str | None = "61"
branch_labels: str | Sequence[str] | None = None branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None

View file

@ -5,6 +5,7 @@ Revises: 62
Create Date: 2026-01-13 12:23:31.481643 Create Date: 2026-01-13 12:23:31.481643
""" """
from collections.abc import Sequence from collections.abc import Sequence
from sqlalchemy import text from sqlalchemy import text
@ -12,8 +13,8 @@ from sqlalchemy import text
from alembic import op from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '63' revision: str = "63"
down_revision: str | None = '62' down_revision: str | None = "62"
branch_labels: str | Sequence[str] | None = None branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None
@ -34,9 +35,9 @@ def upgrade() -> None:
if old_constraint_exists: if old_constraint_exists:
op.drop_constraint( op.drop_constraint(
'uq_searchspace_user_connector_type', "uq_searchspace_user_connector_type",
'search_source_connectors', "search_source_connectors",
type_='unique' type_="unique",
) )
# Check if new constraint already exists before creating it # Check if new constraint already exists before creating it
@ -51,9 +52,9 @@ def upgrade() -> None:
if not new_constraint_exists: if not new_constraint_exists:
op.create_unique_constraint( op.create_unique_constraint(
'uq_searchspace_user_connector_type_name', "uq_searchspace_user_connector_type_name",
'search_source_connectors', "search_source_connectors",
['search_space_id', 'user_id', 'connector_type', 'name'] ["search_space_id", "user_id", "connector_type", "name"],
) )
@ -73,9 +74,9 @@ def downgrade() -> None:
if new_constraint_exists: if new_constraint_exists:
op.drop_constraint( op.drop_constraint(
'uq_searchspace_user_connector_type_name', "uq_searchspace_user_connector_type_name",
'search_source_connectors', "search_source_connectors",
type_='unique' type_="unique",
) )
# Check if old constraint already exists before creating it # Check if old constraint already exists before creating it
@ -90,7 +91,7 @@ def downgrade() -> None:
if not old_constraint_exists: if not old_constraint_exists:
op.create_unique_constraint( op.create_unique_constraint(
'uq_searchspace_user_connector_type', "uq_searchspace_user_connector_type",
'search_source_connectors', "search_source_connectors",
['search_space_id', 'user_id', 'connector_type'] ["search_space_id", "user_id", "connector_type"],
) )

View file

@ -44,4 +44,3 @@ def downgrade() -> None:
DROP COLUMN IF EXISTS author_id; DROP COLUMN IF EXISTS author_id;
""" """
) )

View file

@ -18,7 +18,9 @@ logger = logging.getLogger(__name__)
class MCPClient: class MCPClient:
"""Client for communicating with an MCP server.""" """Client for communicating with an MCP server."""
def __init__(self, command: str, args: list[str], env: dict[str, str] | None = None): def __init__(
self, command: str, args: list[str], env: dict[str, str] | None = None
):
"""Initialize MCP client. """Initialize MCP client.
Args: Args:
@ -47,15 +49,13 @@ class MCPClient:
# Create server parameters with env # Create server parameters with env
server_params = StdioServerParameters( server_params = StdioServerParameters(
command=self.command, command=self.command, args=self.args, env=server_env
args=self.args,
env=server_env
) )
# Spawn server process and create session # Spawn server process and create session
# Note: Cannot combine these context managers because ClientSession # Note: Cannot combine these context managers because ClientSession
# needs the read/write streams from stdio_client # needs the read/write streams from stdio_client
async with stdio_client(server=server_params) as (read, write): async with stdio_client(server=server_params) as (read, write): # noqa: SIM117
async with ClientSession(read, write) as session: async with ClientSession(read, write) as session:
# Initialize the connection # Initialize the connection
await session.initialize() await session.initialize()
@ -85,7 +85,9 @@ class MCPClient:
""" """
if not self.session: if not self.session:
raise RuntimeError("Not connected to MCP server. Use 'async with client.connect():'") raise RuntimeError(
"Not connected to MCP server. Use 'async with client.connect():'"
)
try: try:
# Call tools/list RPC method # Call tools/list RPC method
@ -93,11 +95,15 @@ class MCPClient:
tools = [] tools = []
for tool in response.tools: for tool in response.tools:
tools.append({ tools.append(
"name": tool.name, {
"description": tool.description or "", "name": tool.name,
"input_schema": tool.inputSchema if hasattr(tool, "inputSchema") else {}, "description": tool.description or "",
}) "input_schema": tool.inputSchema
if hasattr(tool, "inputSchema")
else {},
}
)
logger.info("Listed %d tools from MCP server", len(tools)) logger.info("Listed %d tools from MCP server", len(tools))
return tools return tools
@ -121,10 +127,14 @@ class MCPClient:
""" """
if not self.session: if not self.session:
raise RuntimeError("Not connected to MCP server. Use 'async with client.connect():'") raise RuntimeError(
"Not connected to MCP server. Use 'async with client.connect():'"
)
try: try:
logger.info("Calling MCP tool '%s' with arguments: %s", tool_name, arguments) logger.info(
"Calling MCP tool '%s' with arguments: %s", tool_name, arguments
)
# Call tools/call RPC method # Call tools/call RPC method
response = await self.session.call_tool(tool_name, arguments=arguments) response = await self.session.call_tool(tool_name, arguments=arguments)
@ -147,12 +157,17 @@ class MCPClient:
# Handle validation errors from MCP server responses # Handle validation errors from MCP server responses
# Some MCP servers (like server-memory) return extra fields not in their schema # Some MCP servers (like server-memory) return extra fields not in their schema
if "Invalid structured content" in str(e): if "Invalid structured content" in str(e):
logger.warning("MCP server returned data not matching its schema, but continuing: %s", e) logger.warning(
"MCP server returned data not matching its schema, but continuing: %s",
e,
)
# Try to extract result from error message or return a success message # Try to extract result from error message or return a success message
return "Operation completed (server returned unexpected format)" return "Operation completed (server returned unexpected format)"
raise raise
except (ValueError, TypeError, AttributeError, KeyError) as e: except (ValueError, TypeError, AttributeError, KeyError) as e:
logger.error("Failed to call MCP tool '%s': %s", tool_name, e, exc_info=True) logger.error(
"Failed to call MCP tool '%s': %s", tool_name, e, exc_info=True
)
return f"Error calling tool: {e!s}" return f"Error calling tool: {e!s}"

View file

@ -21,7 +21,8 @@ logger = logging.getLogger(__name__)
def _create_dynamic_input_model_from_schema( def _create_dynamic_input_model_from_schema(
tool_name: str, input_schema: dict[str, Any], tool_name: str,
input_schema: dict[str, Any],
) -> type[BaseModel]: ) -> type[BaseModel]:
"""Create a Pydantic model from MCP tool's JSON schema. """Create a Pydantic model from MCP tool's JSON schema.
@ -49,7 +50,10 @@ def _create_dynamic_input_model_from_schema(
from pydantic import Field from pydantic import Field
if is_required: if is_required:
field_definitions[param_name] = (AnyType, Field(..., description=param_description)) field_definitions[param_name] = (
AnyType,
Field(..., description=param_description),
)
else: else:
field_definitions[param_name] = ( field_definitions[param_name] = (
AnyType | None, AnyType | None,
@ -114,7 +118,8 @@ async def _create_mcp_tool_from_definition(
async def load_mcp_tools( async def load_mcp_tools(
session: AsyncSession, search_space_id: int, session: AsyncSession,
search_space_id: int,
) -> list[StructuredTool]: ) -> list[StructuredTool]:
"""Load all MCP tools from user's active MCP server connectors. """Load all MCP tools from user's active MCP server connectors.
@ -150,7 +155,9 @@ async def load_mcp_tools(
env = server_config.get("env", {}) env = server_config.get("env", {})
if not command: if not command:
logger.warning(f"MCP connector {connector.id} missing command, skipping") logger.warning(
f"MCP connector {connector.id} missing command, skipping"
)
continue continue
# Create MCP client # Create MCP client
@ -168,7 +175,9 @@ async def load_mcp_tools(
# Create LangChain tools from definitions # Create LangChain tools from definitions
for tool_def in tool_definitions: for tool_def in tool_definitions:
try: try:
tool = await _create_mcp_tool_from_definition(tool_def, mcp_client) tool = await _create_mcp_tool_from_definition(
tool_def, mcp_client
)
tools.append(tool) tools.append(tool)
except Exception as e: except Exception as e:
logger.exception( logger.exception(

View file

@ -283,7 +283,8 @@ async def build_tools_async(
): ):
try: try:
mcp_tools = await load_mcp_tools( mcp_tools = await load_mcp_tools(
dependencies["db_session"], dependencies["search_space_id"], dependencies["db_session"],
dependencies["search_space_id"],
) )
tools.extend(mcp_tools) tools.extend(mcp_tools)
logging.info( logging.info(

View file

@ -23,7 +23,9 @@ class SearchSourceConnectorBase(BaseModel):
@field_validator("config") @field_validator("config")
@classmethod @classmethod
def validate_config_for_connector_type( def validate_config_for_connector_type(
cls, config: dict[str, Any], values: dict[str, Any], cls,
config: dict[str, Any],
values: dict[str, Any],
) -> dict[str, Any]: ) -> dict[str, Any]:
connector_type = values.data.get("connector_type") connector_type = values.data.get("connector_type")
return validate_connector_config(connector_type, config) return validate_connector_config(connector_type, config)

View file

@ -34,7 +34,6 @@ from .base import (
) )
from .markdown_processor import add_received_markdown_file_document from .markdown_processor import add_received_markdown_file_document
# Constants for LlamaCloud retry configuration # Constants for LlamaCloud retry configuration
LLAMACLOUD_MAX_RETRIES = 3 LLAMACLOUD_MAX_RETRIES = 3
LLAMACLOUD_BASE_DELAY = 5 # Base delay in seconds for exponential backoff LLAMACLOUD_BASE_DELAY = 5 # Base delay in seconds for exponential backoff

View file

@ -120,4 +120,3 @@ export function ApiKeyContent({ onMenuClick }: ApiKeyContentProps) {
</motion.div> </motion.div>
); );
} }

View file

@ -6,8 +6,8 @@ import { AnimatePresence, motion } from "motion/react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { updateUserMutationAtom } from "@/atoms/user/user-mutation.atoms"; import { updateUserMutationAtom } from "@/atoms/user/user-mutation.atoms";
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";

View file

@ -1,7 +1,7 @@
"use client"; "use client";
import { ArrowLeft, ChevronRight, X } from "lucide-react";
import type { LucideIcon } from "lucide-react"; import type { LucideIcon } from "lucide-react";
import { ArrowLeft, ChevronRight, X } from "lucide-react";
import { AnimatePresence, motion } from "motion/react"; import { AnimatePresence, motion } from "motion/react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -152,4 +152,3 @@ export function UserSettingsSidebar({
</> </>
); );
} }

View file

@ -7,7 +7,7 @@ import { useTranslations } from "next-intl";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { ApiKeyContent } from "./components/ApiKeyContent"; import { ApiKeyContent } from "./components/ApiKeyContent";
import { ProfileContent } from "./components/ProfileContent"; import { ProfileContent } from "./components/ProfileContent";
import { UserSettingsSidebar, type SettingsNavItem } from "./components/UserSettingsSidebar"; import { type SettingsNavItem, UserSettingsSidebar } from "./components/UserSettingsSidebar";
export default function UserSettingsPage() { export default function UserSettingsPage() {
const t = useTranslations("userSettings"); const t = useTranslations("userSettings");

View file

@ -16,4 +16,3 @@ export const updateUserMutationAtom = atomWithMutation((get) => {
}, },
}; };
}); });

View file

@ -109,7 +109,9 @@ const DocumentUploadPopupContent: FC<{
<Upload className="size-4 sm:size-7 text-primary" /> <Upload className="size-4 sm:size-7 text-primary" />
</div> </div>
<div className="flex-1 min-w-0 pr-8 sm:pr-0"> <div className="flex-1 min-w-0 pr-8 sm:pr-0">
<h2 className="text-base sm:text-2xl font-semibold tracking-tight">Upload Documents</h2> <h2 className="text-base sm:text-2xl font-semibold tracking-tight">
Upload Documents
</h2>
<p className="text-xs sm:text-base text-muted-foreground mt-0.5 sm:mt-1 line-clamp-1 sm:line-clamp-none"> <p className="text-xs sm:text-base text-muted-foreground mt-0.5 sm:mt-1 line-clamp-1 sm:line-clamp-none">
Upload and sync your documents to your search space Upload and sync your documents to your search space
</p> </p>

View file

@ -36,11 +36,7 @@ import {
newLLMConfigsAtom, newLLMConfigsAtom,
} from "@/atoms/new-llm-config/new-llm-config-query.atoms"; } from "@/atoms/new-llm-config/new-llm-config-query.atoms";
import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { import { ComposerAddAttachment, ComposerAttachments } from "@/components/assistant-ui/attachment";
ComposerAddAttachment,
ComposerAttachments,
} from "@/components/assistant-ui/attachment";
import { UserMessage } from "@/components/assistant-ui/user-message";
import { ConnectorIndicator } from "@/components/assistant-ui/connector-popup"; import { ConnectorIndicator } from "@/components/assistant-ui/connector-popup";
import { import {
InlineMentionEditor, InlineMentionEditor,
@ -53,6 +49,7 @@ import {
} from "@/components/assistant-ui/thinking-steps"; } from "@/components/assistant-ui/thinking-steps";
import { ToolFallback } from "@/components/assistant-ui/tool-fallback"; import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
import { UserMessage } from "@/components/assistant-ui/user-message";
import { import {
DocumentMentionPicker, DocumentMentionPicker,
type DocumentMentionPickerRef, type DocumentMentionPickerRef,
@ -636,7 +633,6 @@ const AssistantActionBar: FC = () => {
); );
}; };
const EditComposer: FC = () => { const EditComposer: FC = () => {
return ( return (
<MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3"> <MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">

View file

@ -139,30 +139,33 @@ export function DocumentUploadTab({
[acceptedFileTypes] [acceptedFileTypes]
); );
const onDrop = useCallback((acceptedFiles: File[]) => { const onDrop = useCallback(
setFiles((prev) => { (acceptedFiles: File[]) => {
const newFiles = [...prev, ...acceptedFiles]; setFiles((prev) => {
const newFiles = [...prev, ...acceptedFiles];
// Check file count limit // Check file count limit
if (newFiles.length > MAX_FILES) { if (newFiles.length > MAX_FILES) {
toast.error(t("max_files_exceeded"), { toast.error(t("max_files_exceeded"), {
description: t("max_files_exceeded_desc", { max: MAX_FILES }), description: t("max_files_exceeded_desc", { max: MAX_FILES }),
}); });
return prev; return prev;
} }
// Check total size limit // Check total size limit
const newTotalSize = newFiles.reduce((sum, file) => sum + file.size, 0); const newTotalSize = newFiles.reduce((sum, file) => sum + file.size, 0);
if (newTotalSize > MAX_TOTAL_SIZE_BYTES) { if (newTotalSize > MAX_TOTAL_SIZE_BYTES) {
toast.error(t("max_size_exceeded"), { toast.error(t("max_size_exceeded"), {
description: t("max_size_exceeded_desc", { max: MAX_TOTAL_SIZE_MB }), description: t("max_size_exceeded_desc", { max: MAX_TOTAL_SIZE_MB }),
}); });
return prev; return prev;
} }
return newFiles; return newFiles;
}); });
}, [t]); },
[t]
);
const { getRootProps, getInputProps, isDragActive } = useDropzone({ const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop, onDrop,
@ -191,7 +194,10 @@ export function DocumentUploadTab({
const isFileCountLimitReached = files.length >= MAX_FILES; const isFileCountLimitReached = files.length >= MAX_FILES;
const isSizeLimitReached = totalFileSize >= MAX_TOTAL_SIZE_BYTES; const isSizeLimitReached = totalFileSize >= MAX_TOTAL_SIZE_BYTES;
const remainingFiles = MAX_FILES - files.length; const remainingFiles = MAX_FILES - files.length;
const remainingSizeMB = Math.max(0, (MAX_TOTAL_SIZE_BYTES - totalFileSize) / (1024 * 1024)).toFixed(1); const remainingSizeMB = Math.max(
0,
(MAX_TOTAL_SIZE_BYTES - totalFileSize) / (1024 * 1024)
).toFixed(1);
// Track accordion state changes // Track accordion state changes
const handleAccordionChange = useCallback( const handleAccordionChange = useCallback(
@ -243,7 +249,8 @@ export function DocumentUploadTab({
<Alert className="border border-border bg-slate-400/5 dark:bg-white/5 flex items-start gap-3 [&>svg]:relative [&>svg]:left-0 [&>svg]:top-0 [&>svg~*]:pl-0"> <Alert className="border border-border bg-slate-400/5 dark:bg-white/5 flex items-start gap-3 [&>svg]:relative [&>svg]:left-0 [&>svg]:top-0 [&>svg~*]:pl-0">
<Info className="h-4 w-4 shrink-0 mt-0.5" /> <Info className="h-4 w-4 shrink-0 mt-0.5" />
<AlertDescription className="text-xs sm:text-sm leading-relaxed pt-0.5"> <AlertDescription className="text-xs sm:text-sm leading-relaxed pt-0.5">
{t("file_size_limit")} {t("upload_limits", { maxFiles: MAX_FILES, maxSizeMB: MAX_TOTAL_SIZE_MB })} {t("file_size_limit")}{" "}
{t("upload_limits", { maxFiles: MAX_FILES, maxSizeMB: MAX_TOTAL_SIZE_MB })}
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@ -270,7 +277,9 @@ export function DocumentUploadTab({
<div className="flex flex-col items-center gap-2 sm:gap-4 text-center px-4"> <div className="flex flex-col items-center gap-2 sm:gap-4 text-center px-4">
<Upload className="h-8 w-8 sm:h-12 sm:w-12 text-destructive/70" /> <Upload className="h-8 w-8 sm:h-12 sm:w-12 text-destructive/70" />
<div> <div>
<p className="text-sm sm:text-lg font-medium text-destructive">{t("file_limit_reached")}</p> <p className="text-sm sm:text-lg font-medium text-destructive">
{t("file_limit_reached")}
</p>
<p className="text-xs sm:text-sm text-muted-foreground mt-1"> <p className="text-xs sm:text-sm text-muted-foreground mt-1">
{t("file_limit_reached_desc", { max: MAX_FILES })} {t("file_limit_reached_desc", { max: MAX_FILES })}
</p> </p>

View file

@ -1,7 +1,7 @@
import { import {
getMeResponse, getMeResponse,
updateUserResponse,
type UpdateUserRequest, type UpdateUserRequest,
updateUserResponse,
} from "@/contracts/types/user.types"; } from "@/contracts/types/user.types";
import { baseApiService } from "./base-api.service"; import { baseApiService } from "./base-api.service";