chore: ran linting

This commit is contained in:
Anish Sarkar 2026-02-10 19:06:21 +05:30
parent b44b4497a6
commit f1ce17bde4
15 changed files with 285 additions and 338 deletions

View file

@ -141,4 +141,3 @@ def downgrade() -> None:
ALTER TABLE image_generation_configs DROP COLUMN IF EXISTS user_id;
"""
)

View file

@ -439,7 +439,9 @@ async def list_snapshots_for_search_space(
"message_count": len(s.message_ids) if s.message_ids else 0,
"thread_id": s.thread_id,
"thread_title": thread_titles.get(s.thread_id, "Untitled"),
"created_by_user_id": str(s.created_by_user_id) if s.created_by_user_id else None,
"created_by_user_id": str(s.created_by_user_id)
if s.created_by_user_id
else None,
}
for s in snapshots
]

View file

@ -651,9 +651,7 @@ async def index_discord_messages(
# PHASE 2: Process each batch document one by one
# Each document transitions: pending → processing → ready/failed
# =======================================================================
logger.info(
f"Phase 2: Processing {len(batches_to_process)} batch documents"
)
logger.info(f"Phase 2: Processing {len(batches_to_process)} batch documents")
for item in batches_to_process:
# Send heartbeat periodically

View file

@ -357,9 +357,7 @@ async def index_slack_messages(
# Group messages into batches of SLACK_BATCH_SIZE
# Each batch becomes a single document with conversation context
# =======================================================
for batch_start in range(
0, len(formatted_messages), SLACK_BATCH_SIZE
):
for batch_start in range(0, len(formatted_messages), SLACK_BATCH_SIZE):
batch = formatted_messages[
batch_start : batch_start + SLACK_BATCH_SIZE
]
@ -377,9 +375,7 @@ async def index_slack_messages(
# channel_id + first message ts + last message ts
first_msg_ts = batch[0].get("timestamp", "")
last_msg_ts = batch[-1].get("timestamp", "")
unique_identifier = (
f"{channel_id}_{first_msg_ts}_{last_msg_ts}"
)
unique_identifier = f"{channel_id}_{first_msg_ts}_{last_msg_ts}"
unique_identifier_hash = generate_unique_identifier_hash(
DocumentType.SLACK_CONNECTOR,
unique_identifier,
@ -392,10 +388,8 @@ async def index_slack_messages(
)
# Check if document with this unique identifier already exists
existing_document = (
await check_document_by_unique_identifier(
session, unique_identifier_hash
)
existing_document = await check_document_by_unique_identifier(
session, unique_identifier_hash
)
if existing_document:
@ -405,9 +399,7 @@ async def index_slack_messages(
if not DocumentStatus.is_state(
existing_document.status, DocumentStatus.READY
):
existing_document.status = (
DocumentStatus.ready()
)
existing_document.status = DocumentStatus.ready()
documents_skipped += 1
continue
@ -440,10 +432,8 @@ async def index_slack_messages(
# Document doesn't exist by unique_identifier_hash
# Check if a document with the same content_hash exists (from another connector)
with session.no_autoflush:
duplicate_by_content = (
await check_duplicate_document_by_hash(
session, content_hash
)
duplicate_by_content = await check_duplicate_document_by_hash(
session, content_hash
)
if duplicate_by_content:
@ -496,12 +486,8 @@ async def index_slack_messages(
"channel_id": channel_id,
"first_message_ts": first_msg_ts,
"last_message_ts": last_msg_ts,
"first_message_time": batch[0].get(
"datetime", "Unknown"
),
"last_message_time": batch[-1].get(
"datetime", "Unknown"
),
"first_message_time": batch[0].get("datetime", "Unknown"),
"last_message_time": batch[-1].get("datetime", "Unknown"),
"message_count": len(batch),
"start_date": start_date_str,
"end_date": end_date_str,
@ -538,9 +524,7 @@ async def index_slack_messages(
# PHASE 2: Process each batch document one by one
# Each document transitions: pending → processing → ready/failed
# =======================================================================
logger.info(
f"Phase 2: Processing {len(batches_to_process)} batch documents"
)
logger.info(f"Phase 2: Processing {len(batches_to_process)} batch documents")
for item in batches_to_process:
# Send heartbeat periodically
@ -621,9 +605,7 @@ async def index_slack_messages(
)
try:
await session.commit()
logger.info(
"Successfully committed all Slack document changes to database"
)
logger.info("Successfully committed all Slack document changes to database")
except Exception as e:
# Handle any remaining integrity errors gracefully (race conditions, etc.)
if (

View file

@ -51,8 +51,7 @@ export function RowActions({
document.status?.state === "pending" || document.status?.state === "processing";
// FILE documents that failed processing cannot be edited
const isFileFailed =
document.document_type === "FILE" && document.status?.state === "failed";
const isFileFailed = document.document_type === "FILE" && document.status?.state === "failed";
// SURFSENSE_DOCS are system-managed and should not show delete at all
const shouldShowDelete = !NON_DELETABLE_DOCUMENT_TYPES.includes(
@ -212,7 +211,8 @@ export function RowActions({
<AlertDialogHeader>
<AlertDialogTitle>Delete document?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete this document from your search space.
This action cannot be undone. This will permanently delete this document from your
search space.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>

View file

@ -1,15 +1,7 @@
"use client";
import { useAtomValue } from "jotai";
import {
Bot,
Check,
ChevronDown,
Edit3,
ImageIcon,
Plus,
Zap,
} from "lucide-react";
import { Bot, Check, ChevronDown, Edit3, ImageIcon, Plus, Zap } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
import { toast } from "sonner";
import {
@ -77,10 +69,10 @@ export function ModelSelector({
// Image data
const { data: imageGlobalConfigs, isLoading: imageGlobalLoading } =
useAtomValue(globalImageGenConfigsAtom);
const { data: imageUserConfigs, isLoading: imageUserLoading } =
useAtomValue(imageGenConfigsAtom);
const { data: imageUserConfigs, isLoading: imageUserLoading } = useAtomValue(imageGenConfigsAtom);
const isLoading = llmUserLoading || llmGlobalLoading || prefsLoading || imageGlobalLoading || imageUserLoading;
const isLoading =
llmUserLoading || llmGlobalLoading || prefsLoading || imageGlobalLoading || imageUserLoading;
// ─── LLM current config ───
const currentLLMConfig = useMemo(() => {
@ -108,7 +100,9 @@ export function ModelSelector({
}, [preferences, imageGlobalConfigs, imageUserConfigs]);
const isImageAutoMode = useMemo(() => {
return currentImageConfig && "is_auto_mode" in currentImageConfig && currentImageConfig.is_auto_mode;
return (
currentImageConfig && "is_auto_mode" in currentImageConfig && currentImageConfig.is_auto_mode
);
}, [currentImageConfig]);
// ─── LLM filtering ───
@ -244,7 +238,9 @@ export function ModelSelector({
{/* LLM section */}
{currentLLMConfig ? (
<>
{getProviderIcon(currentLLMConfig.provider, { isAutoMode: isLLMAutoMode ?? false })}
{getProviderIcon(currentLLMConfig.provider, {
isAutoMode: isLLMAutoMode ?? false,
})}
<span className="max-w-[100px] md:max-w-[120px] truncate hidden md:inline">
{currentLLMConfig.name}
</span>
@ -262,7 +258,9 @@ export function ModelSelector({
{/* Image section */}
{currentImageConfig ? (
<>
{getProviderIcon(currentImageConfig.provider, { isAutoMode: isImageAutoMode ?? false })}
{getProviderIcon(currentImageConfig.provider, {
isAutoMode: isImageAutoMode ?? false,
})}
<span className="max-w-[80px] md:max-w-[100px] truncate hidden md:inline">
{currentImageConfig.name}
</span>
@ -373,7 +371,9 @@ export function ModelSelector({
Recommended
</Badge>
)}
{isSelected && <Check className="size-3.5 text-primary shrink-0" />}
{isSelected && (
<Check className="size-3.5 text-primary shrink-0" />
)}
</div>
<div className="flex items-center gap-1.5 mt-0.5">
<span className="text-xs text-muted-foreground truncate">
@ -436,7 +436,9 @@ export function ModelSelector({
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="font-medium truncate">{config.name}</span>
{isSelected && <Check className="size-3.5 text-primary shrink-0" />}
{isSelected && (
<Check className="size-3.5 text-primary shrink-0" />
)}
</div>
<div className="flex items-center gap-1.5 mt-0.5">
<span className="text-xs text-muted-foreground truncate">
@ -489,7 +491,10 @@ export function ModelSelector({
{/* ─── Image Tab ─── */}
<TabsContent value="image" className="mt-0">
<Command shouldFilter={false} className="rounded-none rounded-b-lg [&_[data-slot=command-input-wrapper]]:border-0 [&_[data-slot=command-input-wrapper]]:px-0 [&_[data-slot=command-input-wrapper]]:gap-2">
<Command
shouldFilter={false}
className="rounded-none rounded-b-lg [&_[data-slot=command-input-wrapper]]:border-0 [&_[data-slot=command-input-wrapper]]:px-0 [&_[data-slot=command-input-wrapper]]:gap-2"
>
{totalImageModels > 3 && (
<div className="px-2 md:px-3 py-1.5 md:py-2">
<CommandInput
@ -573,7 +578,9 @@ export function ModelSelector({
{/* User Image Configs */}
{filteredImageUser.length > 0 && (
<>
{filteredImageGlobal.length > 0 && <CommandSeparator className="my-1 mx-4 bg-border/60" />}
{filteredImageGlobal.length > 0 && (
<CommandSeparator className="my-1 mx-4 bg-border/60" />
)}
<CommandGroup>
<div className="flex items-center gap-2 px-3 py-2 text-xs font-semibold text-muted-foreground tracking-wider">
Your Image Models
@ -591,13 +598,13 @@ export function ModelSelector({
)}
>
<div className="flex items-center gap-3 min-w-0 flex-1">
<div className="shrink-0">
{getProviderIcon(config.provider)}
</div>
<div className="shrink-0">{getProviderIcon(config.provider)}</div>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="font-medium truncate">{config.name}</span>
{isSelected && <Check className="size-3.5 text-primary shrink-0" />}
{isSelected && (
<Check className="size-3.5 text-primary shrink-0" />
)}
</div>
<span className="text-xs text-muted-foreground truncate block">
{config.model_name}

View file

@ -50,9 +50,7 @@ export function PublicChatSnapshotRow({
day: "numeric",
});
const member = snapshot.created_by_user_id
? memberMap.get(snapshot.created_by_user_id)
: null;
const member = snapshot.created_by_user_id ? memberMap.get(snapshot.created_by_user_id) : null;
return (
<Card className="group relative overflow-hidden transition-all duration-200 border-border/60 hover:shadow-md h-full">
@ -77,11 +75,7 @@ export function PublicChatSnapshotRow({
asChild
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<a
href={snapshot.public_url}
target="_blank"
rel="noopener noreferrer"
>
<a href={snapshot.public_url} target="_blank" rel="noopener noreferrer">
<ExternalLink className="h-3 w-3" />
</a>
</Button>
@ -110,51 +104,49 @@ export function PublicChatSnapshotRow({
</div>
</div>
{/* Message count badge */}
<div className="flex items-center gap-1.5">
<Badge
variant="outline"
className="text-[10px] px-1.5 py-0.5 border-muted-foreground/20 text-muted-foreground"
>
<MessageSquare className="h-2.5 w-2.5 mr-1" />
{snapshot.message_count} messages
</Badge>
</div>
{/* Message count badge */}
<div className="flex items-center gap-1.5">
<Badge
variant="outline"
className="text-[10px] px-1.5 py-0.5 border-muted-foreground/20 text-muted-foreground"
>
<MessageSquare className="h-2.5 w-2.5 mr-1" />
{snapshot.message_count} messages
</Badge>
</div>
{/* Public URL selectable fallback for manual copy */}
<div className="flex items-center gap-2 rounded-md border border-border/60 bg-muted/30 px-2.5 py-1.5">
<p
className="min-w-0 flex-1 text-[10px] font-mono text-muted-foreground break-all select-all cursor-text"
title={snapshot.public_url}
>
{snapshot.public_url}
</p>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={handleCopyClick}
className="h-6 w-6 shrink-0 text-muted-foreground hover:text-foreground"
>
{copied ? (
<Check className="h-3 w-3 text-green-500" />
) : (
<Copy className="h-3 w-3" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>{copied ? "Copied!" : "Copy link"}</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
{/* Public URL selectable fallback for manual copy */}
<div className="flex items-center gap-2 rounded-md border border-border/60 bg-muted/30 px-2.5 py-1.5">
<p
className="min-w-0 flex-1 text-[10px] font-mono text-muted-foreground break-all select-all cursor-text"
title={snapshot.public_url}
>
{snapshot.public_url}
</p>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={handleCopyClick}
className="h-6 w-6 shrink-0 text-muted-foreground hover:text-foreground"
>
{copied ? (
<Check className="h-3 w-3 text-green-500" />
) : (
<Copy className="h-3 w-3" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>{copied ? "Copied!" : "Copy link"}</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
{/* Footer: Date + Creator */}
{/* Footer: Date + Creator */}
<div className="flex items-center gap-2 pt-2 border-t border-border/40 mt-auto">
<span className="text-[11px] text-muted-foreground/60">
{formattedDate}
</span>
<span className="text-[11px] text-muted-foreground/60">{formattedDate}</span>
{member && (
<>
<span className="text-muted-foreground/30">·</span>
@ -182,9 +174,7 @@ export function PublicChatSnapshotRow({
</span>
</div>
</TooltipTrigger>
<TooltipContent side="bottom">
{member.email || member.name}
</TooltipContent>
<TooltipContent side="bottom">{member.email || member.name}</TooltipContent>
</Tooltip>
</TooltipProvider>
</>

View file

@ -97,13 +97,13 @@ export function PublicChatSnapshotsManager({
<div className="flex items-start justify-between gap-2">
<Skeleton className="h-4 w-36 md:w-44" />
</div>
{/* Message count badge */}
<div className="flex items-center gap-1.5">
<Skeleton className="h-5 w-24 rounded-full" />
</div>
{/* URL skeleton */}
<Skeleton className="h-3 w-full rounded" />
{/* Footer: Date + Creator */}
{/* Message count badge */}
<div className="flex items-center gap-1.5">
<Skeleton className="h-5 w-24 rounded-full" />
</div>
{/* URL skeleton */}
<Skeleton className="h-3 w-full rounded" />
{/* Footer: Date + Creator */}
<div className="flex items-center gap-2 pt-2 border-t border-border/40">
<Skeleton className="h-3 w-20" />
<Skeleton className="h-4 w-4 rounded-full" />

View file

@ -348,16 +348,16 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
{/* Global info */}
{globalConfigs.filter((g) => !("is_auto_mode" in g && g.is_auto_mode)).length > 0 && (
<Alert className="bg-muted/50 py-3">
<Info className="h-3 w-3 md:h-4 md:w-4 shrink-0" />
<AlertDescription className="text-xs md:text-sm">
<span className="font-medium">
{globalConfigs.filter((g) => !("is_auto_mode" in g && g.is_auto_mode)).length} global
image model(s)
</span>{" "}
available from your administrator.
</AlertDescription>
</Alert>
<Alert className="bg-muted/50 py-3">
<Info className="h-3 w-3 md:h-4 md:w-4 shrink-0" />
<AlertDescription className="text-xs md:text-sm">
<span className="font-medium">
{globalConfigs.filter((g) => !("is_auto_mode" in g && g.is_auto_mode)).length} global
image model(s)
</span>{" "}
available from your administrator.
</AlertDescription>
</Alert>
)}
{/* Loading Skeleton */}
@ -417,7 +417,11 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
: "No image models have been added to this space yet. Contact a space owner to add one."}
</p>
{canCreate && (
<Button onClick={openNewDialog} size="lg" className="gap-2 text-xs md:text-sm h-9 md:h-10">
<Button
onClick={openNewDialog}
size="lg"
className="gap-2 text-xs md:text-sm h-9 md:h-10"
>
<Plus className="h-3 w-3 md:h-4 md:w-4" />
Add First Image Model
</Button>
@ -457,43 +461,43 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
)}
</div>
{(canUpdate || canDelete) && (
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
{canUpdate && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => openEditDialog(config)}
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<Edit3 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{canDelete && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setConfigToDelete(config)}
className="h-7 w-7 text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
)}
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
{canUpdate && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => openEditDialog(config)}
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<Edit3 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{canDelete && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setConfigToDelete(config)}
className="h-7 w-7 text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
)}
</div>
{/* Provider + Model */}
@ -507,14 +511,11 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
{/* Footer: Date + Creator */}
<div className="flex items-center gap-2 pt-2 border-t border-border/40 mt-auto">
<span className="text-[11px] text-muted-foreground/60">
{new Date(config.created_at).toLocaleDateString(
undefined,
{
year: "numeric",
month: "short",
day: "numeric",
}
)}
{new Date(config.created_at).toLocaleDateString(undefined, {
year: "numeric",
month: "short",
day: "numeric",
})}
</span>
{member && (
<>
@ -574,13 +575,11 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
}}
>
<DialogContent
className="max-w-lg max-h-[90vh] overflow-y-auto"
onOpenAutoFocus={(e) => e.preventDefault()}
>
className="max-w-lg max-h-[90vh] overflow-y-auto"
onOpenAutoFocus={(e) => e.preventDefault()}
>
<DialogHeader>
<DialogTitle>
{editingConfig ? "Edit Image Model" : "Add Image Model"}
</DialogTitle>
<DialogTitle>{editingConfig ? "Edit Image Model" : "Add Image Model"}</DialogTitle>
<DialogDescription>
{editingConfig
? "Update your image generation model"

View file

@ -210,8 +210,18 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
...(userImageConfigs ?? []).filter((config) => config.id && config.id.toString().trim() !== ""),
];
const isLoading = configsLoading || preferencesLoading || globalConfigsLoading || imageConfigsLoading || globalImageConfigsLoading;
const hasError = configsError || preferencesError || globalConfigsError || imageConfigsError || globalImageConfigsError;
const isLoading =
configsLoading ||
preferencesLoading ||
globalConfigsLoading ||
imageConfigsLoading ||
globalImageConfigsLoading;
const hasError =
configsError ||
preferencesError ||
globalConfigsError ||
imageConfigsError ||
globalImageConfigsError;
const hasAnyConfigs = allLLMConfigs.length > 0 || allImageConfigs.length > 0;
return (
@ -253,8 +263,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
<AlertDescription className="text-xs md:text-sm">
{(configsError?.message ?? "Failed to load LLM configurations") ||
(preferencesError?.message ?? "Failed to load preferences") ||
(globalConfigsError?.message ??
"Failed to load global configurations")}
(globalConfigsError?.message ?? "Failed to load global configurations")}
</AlertDescription>
</Alert>
</motion.div>
@ -305,8 +314,8 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
<Alert variant="destructive" className="py-3 md:py-4">
<AlertCircle className="h-3 w-3 md:h-4 md:w-4 shrink-0" />
<AlertDescription className="text-xs md:text-sm">
No configurations found. Please add at least one LLM provider or image model
in the respective settings tabs before assigning roles.
No configurations found. Please add at least one LLM provider or image model in the
respective settings tabs before assigning roles.
</AlertDescription>
</Alert>
)}
@ -322,8 +331,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
{Object.entries(ROLE_DESCRIPTIONS).map(([key, role], index) => {
const IconComponent = role.icon;
const isImageRole = role.configType === "image";
const currentAssignment =
assignments[role.prefKey as keyof typeof assignments];
const currentAssignment = assignments[role.prefKey as keyof typeof assignments];
// Pick the right config lists based on role type
const roleGlobalConfigs = isImageRole ? globalImageConfigs : globalConfigs;
@ -332,17 +340,13 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
: newLLMConfigs.filter((c) => c.id && c.id.toString().trim() !== "");
const roleAllConfigs = isImageRole ? allImageConfigs : allLLMConfigs;
const assignedConfig = roleAllConfigs.find(
(config) => config.id === currentAssignment
);
const assignedConfig = roleAllConfigs.find((config) => config.id === currentAssignment);
const isAssigned =
currentAssignment !== "" &&
currentAssignment !== null &&
currentAssignment !== undefined;
const isAutoMode =
assignedConfig &&
"is_auto_mode" in assignedConfig &&
assignedConfig.is_auto_mode;
assignedConfig && "is_auto_mode" in assignedConfig && assignedConfig.is_auto_mode;
return (
<motion.div
@ -362,14 +366,10 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
role.bgColor
)}
>
<IconComponent
className={cn("w-4 h-4", role.color)}
/>
<IconComponent className={cn("w-4 h-4", role.color)} />
</div>
<div className="min-w-0">
<h4 className="text-sm font-semibold tracking-tight">
{role.title}
</h4>
<h4 className="text-sm font-semibold tracking-tight">{role.title}</h4>
<p className="text-[11px] text-muted-foreground/70 mt-0.5">
{role.description}
</p>
@ -389,9 +389,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
</Label>
<Select
value={currentAssignment?.toString() || "unassigned"}
onValueChange={(value) =>
handleRoleAssignment(role.prefKey, value)
}
onValueChange={(value) => handleRoleAssignment(role.prefKey, value)}
>
<SelectTrigger className="w-full h-9 md:h-10 text-xs md:text-sm">
<SelectValue placeholder="Select a configuration" />
@ -401,9 +399,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
value="unassigned"
className="text-xs md:text-sm py-1.5 md:py-2"
>
<span className="text-muted-foreground">
Unassigned
</span>
<span className="text-muted-foreground">Unassigned</span>
</SelectItem>
{/* Global Configurations */}
@ -413,9 +409,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
Global Configurations
</SelectLabel>
{roleGlobalConfigs.map((config) => {
const isAuto =
"is_auto_mode" in config &&
config.is_auto_mode;
const isAuto = "is_auto_mode" in config && config.is_auto_mode;
return (
<SelectItem
key={config.id}
@ -432,18 +426,16 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
AUTO
</Badge>
) : (
getProviderIcon(config.provider, { className: "size-3 md:size-3.5 shrink-0" })
getProviderIcon(config.provider, {
className: "size-3 md:size-3.5 shrink-0",
})
)}
<span className="truncate text-xs md:text-sm">
{config.name}
</span>
{!isAuto && (
<span className="text-muted-foreground text-[10px] md:text-[11px] truncate">
(
{
config.model_name
}
)
({config.model_name})
</span>
)}
{isAuto && (
@ -468,42 +460,40 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
Your Configurations
</SelectLabel>
{roleUserConfigs.map((config) => (
<SelectItem
key={config.id}
value={config.id.toString()}
className="text-xs md:text-sm py-1.5 md:py-2"
>
<div className="flex items-center gap-1 md:gap-1.5 flex-wrap min-w-0">
{getProviderIcon(config.provider, { className: "size-3 md:size-3.5 shrink-0" })}
<span className="truncate text-xs md:text-sm">
{config.name}
</span>
<span className="text-muted-foreground text-[10px] md:text-[11px] truncate">
(
{
config.model_name
}
)
</span>
</div>
</SelectItem>
))}
<SelectItem
key={config.id}
value={config.id.toString()}
className="text-xs md:text-sm py-1.5 md:py-2"
>
<div className="flex items-center gap-1 md:gap-1.5 flex-wrap min-w-0">
{getProviderIcon(config.provider, {
className: "size-3 md:size-3.5 shrink-0",
})}
<span className="truncate text-xs md:text-sm">
{config.name}
</span>
<span className="text-muted-foreground text-[10px] md:text-[11px] truncate">
({config.model_name})
</span>
</div>
</SelectItem>
))}
</SelectGroup>
)}
</SelectContent>
</Select>
</div>
{/* Assigned Config Summary */}
{assignedConfig && (
<div
className={cn(
"rounded-lg p-3 border",
isAutoMode
? "bg-violet-50 dark:bg-violet-900/10 border-violet-200/50 dark:border-violet-800/30"
: "bg-muted/40 border-border/50"
)}
>
{/* Assigned Config Summary */}
{assignedConfig && (
<div
className={cn(
"rounded-lg p-3 border",
isAutoMode
? "bg-violet-50 dark:bg-violet-900/10 border-violet-200/50 dark:border-violet-800/30"
: "bg-muted/40 border-border/50"
)}
>
{isAutoMode ? (
<div className="flex items-center gap-2">
<Shuffle
@ -525,21 +515,17 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
<IconComponent className="w-3.5 h-3.5 shrink-0 mt-0.5 text-muted-foreground" />
<div className="min-w-0 flex-1">
<div className="flex items-center gap-1.5 flex-wrap">
<span className="text-xs font-medium">
{assignedConfig.name}
</span>
{"is_global" in assignedConfig &&
assignedConfig.is_global && (
<Badge
variant="secondary"
className="text-[9px] px-1.5 py-0"
>
🌐 Global
</Badge>
)}
<span className="text-xs font-medium">{assignedConfig.name}</span>
{"is_global" in assignedConfig && assignedConfig.is_global && (
<Badge variant="secondary" className="text-[9px] px-1.5 py-0">
🌐 Global
</Badge>
)}
</div>
<div className="flex items-center gap-1.5 mt-1">
{getProviderIcon(assignedConfig.provider, { className: "size-3 shrink-0" })}
{getProviderIcon(assignedConfig.provider, {
className: "size-3 shrink-0",
})}
<code className="text-[10px] text-muted-foreground font-mono truncate">
{assignedConfig.model_name}
</code>
@ -552,9 +538,9 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
</div>
</div>
)}
</div>
)}
</CardContent>
</div>
)}
</CardContent>
</Card>
</motion.div>
);
@ -572,9 +558,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
transition={{ duration: 0.2 }}
className="flex items-center justify-between gap-3 rounded-lg border border-border bg-muted/50 p-3 md:p-4"
>
<p className="text-xs md:text-sm text-muted-foreground">
You have unsaved changes
</p>
<p className="text-xs md:text-sm text-muted-foreground">You have unsaved changes</p>
<div className="flex items-center gap-2">
<Button
variant="outline"

View file

@ -83,18 +83,15 @@ function getInitials(name: string): string {
export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
// Mutations
const {
mutateAsync: createConfig,
isPending: isCreating,
} = useAtomValue(createNewLLMConfigMutationAtom);
const {
mutateAsync: updateConfig,
isPending: isUpdating,
} = useAtomValue(updateNewLLMConfigMutationAtom);
const {
mutateAsync: deleteConfig,
isPending: isDeleting,
} = useAtomValue(deleteNewLLMConfigMutationAtom);
const { mutateAsync: createConfig, isPending: isCreating } = useAtomValue(
createNewLLMConfigMutationAtom
);
const { mutateAsync: updateConfig, isPending: isUpdating } = useAtomValue(
updateNewLLMConfigMutationAtom
);
const { mutateAsync: deleteConfig, isPending: isDeleting } = useAtomValue(
deleteNewLLMConfigMutationAtom
);
// Queries
const {
@ -380,43 +377,43 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
)}
</div>
{(canUpdate || canDelete) && (
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
{canUpdate && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => openEditDialog(config)}
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<Edit3 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{canDelete && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setConfigToDelete(config)}
className="h-7 w-7 text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
)}
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
{canUpdate && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => openEditDialog(config)}
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<Edit3 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{canDelete && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setConfigToDelete(config)}
className="h-7 w-7 text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
)}
</div>
{/* Provider + Model */}
@ -453,14 +450,11 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
{/* Footer: Date + Creator */}
<div className="flex items-center gap-2 pt-2 border-t border-border/40 mt-auto">
<span className="text-[11px] text-muted-foreground/60">
{new Date(config.created_at).toLocaleDateString(
undefined,
{
year: "numeric",
month: "short",
day: "numeric",
}
)}
{new Date(config.created_at).toLocaleDateString(undefined, {
year: "numeric",
month: "short",
day: "numeric",
})}
</span>
{member && (
<>

View file

@ -218,7 +218,9 @@ export const getImageGenConfigsResponse = z.array(imageGenerationConfig);
export const updateImageGenConfigRequest = z.object({
id: z.number(),
data: imageGenerationConfig.omit({ id: true, created_at: true, search_space_id: true, user_id: true }).partial(),
data: imageGenerationConfig
.omit({ id: true, created_at: true, search_space_id: true, user_id: true })
.partial(),
});
export const updateImageGenConfigResponse = imageGenerationConfig;

View file

@ -1,7 +1,4 @@
import {
Bot,
Shuffle,
} from "lucide-react";
import { Bot, Shuffle } from "lucide-react";
import { cn } from "@/lib/utils";
import { Ai21Icon } from "@/components/icons/providers";
import { AnthropicIcon } from "@/components/icons/providers";
@ -41,10 +38,7 @@ import { ZhipuIcon } from "@/components/icons/providers";
*/
export function getProviderIcon(
provider: string,
{
isAutoMode,
className = "size-4",
}: { isAutoMode?: boolean; className?: string } = {}
{ isAutoMode, className = "size-4" }: { isAutoMode?: boolean; className?: string } = {}
) {
if (isAutoMode || provider?.toUpperCase() === "AUTO") {
return <Shuffle className={cn(className, "text-violet-800")} />;
@ -123,4 +117,3 @@ export function getProviderIcon(
return <Bot className={cn(className, "text-muted-foreground")} />;
}
}

View file

@ -41,9 +41,7 @@ const nextConfig: NextConfig = {
}
// SVGR: import *.svg as React components
const fileLoaderRule = config.module.rules.find(
(rule: any) => rule.test?.test?.(".svg"),
);
const fileLoaderRule = config.module.rules.find((rule: any) => rule.test?.test?.(".svg"));
config.module.rules.push(
// Re-apply the existing file loader for *.svg?url imports
{
@ -57,7 +55,7 @@ const nextConfig: NextConfig = {
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] },
use: ["@svgr/webpack"],
},
}
);
fileLoaderRule.exclude = /\.svg$/i;

View file

@ -3,4 +3,3 @@ declare module "*.svg" {
const content: FC<SVGProps<SVGSVGElement>>;
export default content;
}