CoolFace
Apppublic

Exched/DeepSeek_Coder

sourceHugging Facemitupdated 1y agoView on Hugging Face
0likes
settings.tsx290 linesDownload Raw Back to ask-ai
1"use client";2import classNames from "classnames";3 4import {5  Popover,6  PopoverContent,7  PopoverTrigger,8} from "@/components/ui/popover";9import { PROVIDERS, MODELS } from "@/lib/providers";10import { Button } from "@/components/ui/button";11import {12  Select,13  SelectContent,14  SelectGroup,15  SelectItem,16  SelectLabel,17  SelectTrigger,18  SelectValue,19} from "@/components/ui/select";20import { useMemo, useState, useEffect } from "react";21import { useUpdateEffect } from "react-use";22import Image from "next/image";23import { Brain, BrainIcon, CheckCheck, ChevronDown } from "lucide-react";24import { useAi } from "@/hooks/useAi";25import { getProviders } from "@/lib/get-providers";26import Loading from "@/components/loading";27 28export function Settings({29  open,30  onClose,31  error,32  isFollowUp = false,33}: {34  open: boolean;35  error?: string;36  isFollowUp?: boolean;37  onClose: React.Dispatch<React.SetStateAction<boolean>>;38}) {39  const {40    model,41    provider,42    setProvider,43    setModel,44    selectedModel,45    globalAiLoading,46  } = useAi();47  const [isMounted, setIsMounted] = useState(false);48  const [loadingProviders, setLoadingProviders] = useState(false);49 50  useEffect(() => {51    setIsMounted(true);52  }, []);53 54  // const modelAvailableProviders = useMemo(() => {55  //   const availableProviders = MODELS.find(56  //     (m: { value: string }) => m.value === model57  //   )?.providers;58  //   if (!availableProviders) return Object.keys(PROVIDERS);59  //   return Object.keys(PROVIDERS).filter((id) =>60  //     availableProviders.includes(id)61  //   );62  // }, [model]);63 64  useUpdateEffect(() => {65    if (provider !== "auto" && !providers.includes(provider as string)) {66      setProvider("auto");67    }68  }, [model, provider]);69 70  const formattedModels = useMemo(() => {71    const lists: ((typeof MODELS)[0] | { isCategory: true; name: string })[] =72      [];73    const keys = new Set<string>();74    MODELS.forEach((model) => {75      if (!keys.has(model.companyName)) {76        lists.push({77          isCategory: true,78          name: model.companyName,79          logo: model.logo,80        });81        keys.add(model.companyName);82      }83      lists.push(model);84    });85    return lists;86  }, [MODELS]);87 88  const [providers, setProviders] = useState<any[]>([]);89  const [failedImages, setFailedImages] = useState<Set<string>>(new Set());90 91  useEffect(() => {92    const loadProviders = async () => {93      setLoadingProviders(true);94      if (!model) {95        setProviders([]);96        return;97      }98      try {99        const result = await getProviders(model);100        setProviders(result);101      } catch (error) {102        console.error("Failed to load providers:", error);103        setProviders([]);104      } finally {105        setLoadingProviders(false);106      }107    };108 109    loadProviders();110  }, [model]);111 112  const handleImageError = (providerId: string) => {113    setFailedImages((prev) => new Set([...prev, providerId]));114  };115 116  return (117    <Popover open={open} onOpenChange={onClose}>118      <PopoverTrigger asChild>119        <Button120          variant={open ? "default" : "outline"}121          className="!rounded-md"122          disabled={globalAiLoading || loadingProviders}123          size="xs"124        >125          {/* <Brain className="size-3.5" /> */}126          {selectedModel?.logo && (127            <Image128              src={selectedModel?.logo}129              alt={selectedModel.label}130              className={`size-3.5 ${open ? "" : "filter invert"}`}131              width={20}132              height={20}133            />134          )}135          <span className="truncate max-w-[120px]">136            {isMounted137              ? selectedModel?.label?.split(" ").join("-").toLowerCase()138              : "..."}139          </span>140          <ChevronDown className="size-3.5" />141        </Button>142      </PopoverTrigger>143      <PopoverContent144        className="!rounded-2xl p-0 !w-96 overflow-hidden !bg-neutral-900"145        align="center"146      >147        <header className="flex items-center justify-center text-sm px-4 py-3 border-b gap-2 bg-neutral-950 border-neutral-800 font-semibold text-neutral-200">148          Customize Settings149        </header>150        <main className="px-4 pt-5 pb-6 space-y-5">151          {error !== "" && (152            <p className="text-red-500 text-sm font-medium mb-2 flex items-center justify-between bg-red-500/10 p-2 rounded-md">153              {error}154            </p>155          )}156          <label className="block">157            <p className="text-neutral-300 text-sm mb-2.5">Choose a model</p>158            <Select defaultValue={model} onValueChange={setModel}>159              <SelectTrigger className="w-full">160                <SelectValue placeholder="Select a model" />161              </SelectTrigger>162              <SelectContent>163                <SelectGroup>164                  {formattedModels.map((item: any) => {165                    if ("isCategory" in item) {166                      return (167                        <SelectLabel168                          key={item.name}169                          className="flex items-center gap-1"170                        >171                          {item.name}172                        </SelectLabel>173                      );174                    }175                    const {176                      value,177                      label,178                      isNew = false,179                      isThinker = false,180                    } = item;181                    return (182                      <SelectItem183                        key={value}184                        value={value}185                        className=""186                        disabled={isThinker && isFollowUp}187                      >188                        {label}189                        {isNew && (190                          <span className="text-xs bg-gradient-to-br from-sky-400 to-sky-600 text-white rounded-full px-1.5 py-0.5">191                            New192                          </span>193                        )}194                      </SelectItem>195                    );196                  })}197                </SelectGroup>198              </SelectContent>199            </Select>200          </label>201          {/* {isFollowUp && (202            <div className="bg-amber-500/10 border-amber-500/10 p-3 text-xs text-amber-500 border rounded-lg">203              Note: You can&apos;t use a Thinker model for follow-up requests.204              We automatically switch to the default model for you.205            </div>206          )} */}207          <div className="flex flex-col gap-3">208            <div className="flex items-center justify-between">209              <div>210                <p className="text-neutral-300 text-sm mb-1.5">211                  Use auto-provider212                </p>213                <p className="text-xs text-neutral-400/70">214                  We&apos;ll automatically select the best provider for you215                  based on your prompt.216                </p>217              </div>218              <div219                className={classNames(220                  "bg-neutral-700 rounded-full min-w-10 w-10 h-6 flex items-center justify-between p-1 cursor-pointer transition-all duration-200",221                  {222                    "!bg-sky-500": provider === "auto",223                  }224                )}225                onClick={() => {226                  const foundModel = MODELS.find(227                    (m: { value: string }) => m.value === model228                  );229                  if (provider === "auto" && foundModel?.autoProvider) {230                    setProvider(foundModel.autoProvider);231                  } else {232                    setProvider("auto");233                  }234                }}235              >236                <div237                  className={classNames(238                    "w-4 h-4 rounded-full shadow-md transition-all duration-200 bg-neutral-200",239                    {240                      "translate-x-4": provider === "auto",241                    }242                  )}243                />244              </div>245            </div>246            <label className="block">247              <p className="text-neutral-300 text-sm mb-2">248                Inference Provider249              </p>250              <div className="grid grid-cols-2 gap-1.5 relative">251                {loadingProviders ? (252                  <Loading overlay={false} />253                ) : (254                  providers.map((id: string) => (255                    <Button256                      key={id}257                      variant={id === provider ? "default" : "secondary"}258                      size="sm"259                      onClick={() => {260                        setProvider(id);261                      }}262                    >263                      {failedImages.has(id) ? (264                        <BrainIcon className="size-4 mr-2" />265                      ) : (266                        <Image267                          src={`/providers/${id}.svg`}268                          alt={id}269                          className="size-5 mr-2"270                          width={20}271                          height={20}272                          onError={() => handleImageError(id)}273                        />274                      )}275                      {PROVIDERS?.[id as keyof typeof PROVIDERS]?.name || id}276                      {id === provider && (277                        <CheckCheck className="ml-2 size-4 text-blue-500" />278                      )}279                    </Button>280                  ))281                )}282              </div>283            </label>284          </div>285        </main>286      </PopoverContent>287    </Popover>288  );289}290