chore: update model market

This commit is contained in:
Zhang Minghan 2024-01-22 00:01:25 +08:00
parent c7db899797
commit 2d41885cd6
7 changed files with 200 additions and 200 deletions

View File

@ -15,6 +15,7 @@ export type PopupDialogProps = {
title: string;
description?: string;
name: string;
placeholder?: string;
defaultValue?: string;
onValueChange?: (value: string) => string;
onSubmit?: (value: string) => Promise<boolean>;
@ -44,6 +45,7 @@ function PopupDialog({
title,
description,
name,
placeholder,
defaultValue,
onValueChange,
onSubmit,
@ -71,6 +73,7 @@ function PopupDialog({
);
}}
value={value}
placeholder={placeholder}
/>
</div>
<DialogFooter>

View File

@ -35,7 +35,7 @@ function AppProvider() {
if (!obj) return;
item.free = obj.type === nonBilling;
item.auth = item.free && !obj.anonymous;
item.auth = !item.free || !obj.anonymous;
});
resetJsArray(supportModels, loadPreferenceModels(market));

View File

@ -422,6 +422,7 @@
"update": "更新",
"migrate": "提交",
"sync": "同步上游",
"sync-site": "上游地址",
"sync-tip": "同步上游模型市场",
"sync-placeholder": "请输入上游 Chat Nio 的 API 地址https://api.chatnio.net",
"sync-items": "共发现 {{length}} 个模型,已有模型 {{exist}} 个(不会覆盖),新增模型 {{new}} 个,本站点渠道已支持模型 {{support}} 个",

View File

@ -488,7 +488,8 @@
"sync-placeholder": "Please enter the API address of the upstream Chat Nio, for example: https://api.chatnio.net",
"sync-items": "A total of {{length}} models have been found, {{exist}} models have been found (will not be overwritten), {{new}} models have been added, {{support}} models have been supported in this site channel",
"sync-all": "Sync all ({{length}})",
"sync-self": "Sync supported models ({{length}})"
"sync-self": "Sync supported models ({{length}})",
"sync-site": "Upstream address"
},
"model-chart-tip": "Token usage",
"subscription": "Subscription Management",

View File

@ -488,7 +488,8 @@
"sync-placeholder": "アップストリームのChat NioのAPIアドレスを入力してください。例 https://api.chatnio.net",
"sync-items": "合計{{length}}個のモデルが見つかりました。{{exist}}個のモデルが見つかりました(上書きされません)。{{new}}個のモデルが追加されました。{{support}}個のモデルがこのサイトチャンネルでサポートされています",
"sync-all": "すべて同期({{length}})",
"sync-self": "サポートされているモデルを同期({{ length }}"
"sync-self": "サポートされているモデルを同期({{ length }}",
"sync-site": "アップストリームアドレス"
},
"model-chart-tip": "トークンの使用状況",
"subscription": "サブスクリプション管理",

View File

@ -488,7 +488,8 @@
"sync-placeholder": "Введите API-адрес вышестоящего Chat Nio, например: https://api.chatnio.net",
"sync-items": "Всего было найдено {{length}} моделей, {{exist}} моделей было найдено (не будет перезаписано), {{new}} моделей было добавлено, {{support}} моделей было поддержано в этом канале сайта",
"sync-all": "Синхронизировать все ({{length}})",
"sync-self": "Синхронизация поддерживаемых моделей ({{length}})"
"sync-self": "Синхронизация поддерживаемых моделей ({{length}})",
"sync-site": "Адрес выше по потоку"
},
"model-chart-tip": "Использование токенов",
"subscription": "Управление подписками",

View File

@ -8,9 +8,15 @@ import { useTranslation } from "react-i18next";
import { Dispatch, useMemo, useReducer, useState } from "react";
import { Model as RawModel } from "@/api/types.ts";
import { supportModels } from "@/conf";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
import { Input } from "@/components/ui/input.tsx";
import { GripVertical, HelpCircle, Loader2, Plus, Trash2 } from "lucide-react";
import {
ChevronDown,
ChevronUp,
HelpCircle,
Loader2,
Plus,
Trash2,
} from "lucide-react";
import { generateRandomChar, isUrl } from "@/utils/base.ts";
import Require from "@/components/Require.tsx";
import { Textarea } from "@/components/ui/textarea.tsx";
@ -26,6 +32,7 @@ import { Combobox } from "@/components/ui/combo-box.tsx";
import { channelModels } from "@/admin/channel.ts";
import { cn } from "@/components/ui/lib/utils.ts";
import { marketEvent } from "@/events/market.ts";
import PopupDialog from "@/components/PopupDialog.tsx";
type Model = RawModel & {
seed?: string;
@ -169,6 +176,18 @@ function reducer(state: MarketForm, action: any): MarketForm {
const [removed] = state.splice(from, 1);
state.splice(to, 0, removed);
return [...state];
case "upward":
if (action.payload.idx === 0) return state;
const upward = state[action.payload.idx];
state[action.payload.idx] = state[action.payload.idx - 1];
state[action.payload.idx - 1] = upward;
return [...state];
case "downward":
if (action.payload.idx === state.length - 1) return state;
const downward = state[action.payload.idx];
state[action.payload.idx] = state[action.payload.idx + 1];
state[action.payload.idx + 1] = downward;
return [...state];
default:
throw new Error();
}
@ -339,8 +358,19 @@ function Market() {
}, [form, index]);
};
const [popupOpen, setPopupOpen] = useState<boolean>(false);
return (
<div className={`market`}>
<PopupDialog
title={t("admin.market.sync")}
name={t("admin.market.sync-site")}
placeholder={t("admin.market.sync-placeholder")}
open={popupOpen}
setOpen={setPopupOpen}
defaultValue={"https://api.chatnio.net"}
/>
<Card className={`admin-card market-card`}>
<CardHeader className={`flex flex-row items-center select-none`}>
<CardTitle>
@ -348,209 +378,172 @@ function Market() {
{loading && <Loader2 className={`h-4 w-4 ml-2 animate-spin`} />}
</CardTitle>
<Button
loading={true}
className={`ml-auto mt-0 whitespace-nowrap`}
size={`sm`}
style={{ marginTop: 0 }}
onClick={update}
onClick={() => setPopupOpen(true)}
>
{t("admin.market.migrate")}
{t("admin.market.sync")}
</Button>
</CardHeader>
<CardContent>
<DragDropContext
onDragEnd={(result) => {
const { destination, source } = result;
if (
!destination ||
destination.index === source.index ||
destination.index === -1
)
return;
const from = source.index;
const to = destination.index;
dispatch({ type: "replace", payload: { from, to } });
}}
>
<Droppable droppableId={`admin-market`}>
{(provided) => (
<div
className={`market-list`}
{...provided.droppableProps}
ref={provided.innerRef}
>
{form.map((model, index) => (
<Draggable
draggableId={`admin-model-${model.seed}`}
index={index}
key={model.seed}
>
{(provided) => (
<div
className={cn(
"market-item",
!checked(index) && "error",
)}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<GripVertical className={`drop-icon h-4 w-4 mr-2`} />
<div className={`model-wrapper`}>
<div className={`market-row`}>
<span>
<Require />
{t("admin.market.model-name")}
</span>
<Input
value={model.name}
placeholder={t(
"admin.market.model-name-placeholder",
)}
onChange={(e) => {
dispatch({
type: "update-name",
payload: {
idx: index,
name: e.target.value,
},
});
}}
/>
</div>
<div className={`market-row`}>
<span>
<Require />
{t("admin.market.model-id")}
</span>
<Combobox
value={model.id}
onChange={(id: string) => {
dispatch({
type: "update-id",
payload: { idx: index, id },
});
}}
className={`model-combobox`}
list={channelModels}
placeholder={t(
"admin.market.model-id-placeholder",
)}
/>
</div>
<div className={`market-row`}>
<span>{t("admin.market.model-description")}</span>
<Textarea
value={model.description || ""}
placeholder={t(
"admin.market.model-description-placeholder",
)}
onChange={(e) => {
dispatch({
type: "update-description",
payload: {
idx: index,
description: e.target.value,
},
});
}}
/>
</div>
<div className={`market-row`}>
<span>
{t("admin.market.model-context")}
<Tips
content={t("admin.market.model-context-tip")}
/>
</span>
<Switch
className={`ml-auto`}
checked={model.high_context}
onCheckedChange={(state) => {
dispatch({
type: "update-context",
payload: {
idx: index,
context: state,
},
});
}}
/>
</div>
<div className={`market-row`}>
<span>
{t("admin.market.model-is-default")}
<Tips
content={t(
"admin.market.model-is-default-tip",
)}
/>
</span>
<Switch
className={`ml-auto`}
checked={model.default}
onCheckedChange={(state) => {
dispatch({
type: "update-default",
payload: {
idx: index,
default: state,
},
});
}}
/>
</div>
<div className={`market-row`}>
<span>{t("admin.market.model-tag")}</span>
<MarketTags
tag={model.tag}
idx={index}
dispatch={dispatch}
/>
</div>
<div className={`market-row`}>
<span>{t("admin.market.model-image")}</span>
<MarketImage
image={model.avatar}
idx={index}
dispatch={dispatch}
/>
</div>
<div className={`market-row`}>
<div className={`grow`} />
<Button
variant={`outline`}
size={`icon`}
onClick={() =>
dispatch({
type: "remove",
payload: { idx: index },
})
}
>
<Trash2 className={`h-4 w-4`} />
</Button>
{index === form.length - 1 && (
<Button
size={`icon`}
onClick={() => dispatch({ type: "new" })}
>
<Plus className={`h-4 w-4`} />
</Button>
)}
</div>
</div>
</div>
<div className={`market-list`}>
{form.map((model, index) => (
<div className={cn("market-item", !checked(index) && "error")}>
<div className={`model-wrapper`}>
<div className={`market-row`}>
<span>
<Require />
{t("admin.market.model-name")}
</span>
<Input
value={model.name}
placeholder={t("admin.market.model-name-placeholder")}
onChange={(e) => {
dispatch({
type: "update-name",
payload: {
idx: index,
name: e.target.value,
},
});
}}
/>
</div>
<div className={`market-row`}>
<span>
<Require />
{t("admin.market.model-id")}
</span>
<Combobox
value={model.id}
onChange={(id: string) => {
dispatch({
type: "update-id",
payload: { idx: index, id },
});
}}
className={`model-combobox`}
list={channelModels}
placeholder={t("admin.market.model-id-placeholder")}
/>
</div>
<div className={`market-row`}>
<span>{t("admin.market.model-description")}</span>
<Textarea
value={model.description || ""}
placeholder={t(
"admin.market.model-description-placeholder",
)}
</Draggable>
))}
{provided.placeholder}
onChange={(e) => {
dispatch({
type: "update-description",
payload: {
idx: index,
description: e.target.value,
},
});
}}
/>
</div>
<div className={`market-row`}>
<span>
{t("admin.market.model-context")}
<Tips content={t("admin.market.model-context-tip")} />
</span>
<Switch
className={`ml-auto`}
checked={model.high_context}
onCheckedChange={(state) => {
dispatch({
type: "update-context",
payload: {
idx: index,
context: state,
},
});
}}
/>
</div>
<div className={`market-row`}>
<span>
{t("admin.market.model-is-default")}
<Tips content={t("admin.market.model-is-default-tip")} />
</span>
<Switch
className={`ml-auto`}
checked={model.default}
onCheckedChange={(state) => {
dispatch({
type: "update-default",
payload: {
idx: index,
default: state,
},
});
}}
/>
</div>
<div className={`market-row`}>
<span>{t("admin.market.model-tag")}</span>
<MarketTags
tag={model.tag}
idx={index}
dispatch={dispatch}
/>
</div>
<div className={`market-row`}>
<span>{t("admin.market.model-image")}</span>
<MarketImage
image={model.avatar}
idx={index}
dispatch={dispatch}
/>
</div>
<div className={`market-row`}>
<div className={`grow`} />
<Button
size={`icon`}
variant={`outline`}
onClick={() =>
dispatch({
type: "upward",
payload: { idx: index },
})
}
disabled={index === 0}
>
<ChevronUp className={`h-4 w-4`} />
</Button>
<Button
size={`icon`}
variant={`outline`}
onClick={() =>
dispatch({
type: "downward",
payload: { idx: index },
})
}
disabled={index === form.length - 1}
>
<ChevronDown className={`h-4 w-4`} />
</Button>
<Button
size={`icon`}
onClick={() =>
dispatch({
type: "remove",
payload: { idx: index },
})
}
>
<Trash2 className={`h-4 w-4`} />
</Button>
</div>
</div>
)}
</Droppable>
</DragDropContext>
</div>
))}
</div>
<div className={`market-footer flex flex-row items-center mt-4`}>
<div className={`grow`} />
<Button