From 655f8c31374e91b1c6a9246f92f95b85092c88e1 Mon Sep 17 00:00:00 2001 From: Zhang Minghan Date: Wed, 15 Nov 2023 22:50:08 +0800 Subject: [PATCH] update i18n source and subscription usage --- app/src/admin/colors.ts | 1 + .../components/home/ConversationSegment.tsx | 2 +- app/src/components/home/ModelMarket.tsx | 127 ++++++++++++------ app/src/components/home/SideBar.tsx | 2 +- app/src/conf.ts | 3 +- app/src/conversation/addition.ts | 9 +- app/src/dialogs/Subscription.tsx | 17 ++- app/src/i18n.ts | 36 +++++ auth/rule.go | 5 +- auth/user.go | 10 +- 10 files changed, 155 insertions(+), 57 deletions(-) diff --git a/app/src/admin/colors.ts b/app/src/admin/colors.ts index 811b692..f7b28e9 100644 --- a/app/src/admin/colors.ts +++ b/app/src/admin/colors.ts @@ -6,6 +6,7 @@ export const modelColorMapper: Record = { "gpt-3.5-turbo-1106": "#11ba2b", dalle: "#e4e5e5", "dall-e-2": "#e4e5e5", + "dall-e-3": "#e4e5e5", midjourney: "#7300ff", "midjourney-fast": "#7300ff", diff --git a/app/src/components/home/ConversationSegment.tsx b/app/src/components/home/ConversationSegment.tsx index 919215d..25d7ab2 100644 --- a/app/src/components/home/ConversationSegment.tsx +++ b/app/src/components/home/ConversationSegment.tsx @@ -13,7 +13,7 @@ import { useDispatch } from "react-redux"; import { useTranslation } from "react-i18next"; import { ConversationInstance } from "@/conversation/types.ts"; import { useState } from "react"; -import {closeMarket} from "@/store/chat.ts"; +import { closeMarket } from "@/store/chat.ts"; type ConversationSegmentProps = { conversation: ConversationInstance; diff --git a/app/src/components/home/ModelMarket.tsx b/app/src/components/home/ModelMarket.tsx index b701eb2..1582ff7 100644 --- a/app/src/components/home/ModelMarket.tsx +++ b/app/src/components/home/ModelMarket.tsx @@ -1,15 +1,40 @@ import { useTranslation } from "react-i18next"; import { Input } from "@/components/ui/input.tsx"; -import {ChevronLeft, ChevronRight, Link, Plus, Search, Trash2, X} from "lucide-react"; +import { + ChevronLeft, + ChevronRight, + Link, + Plus, + Search, + Trash2, + X, +} from "lucide-react"; import React, { useMemo, useState } from "react"; -import {modelAvatars, modelPricingLink, planModels, studentModels, supportModels} from "@/conf.ts"; +import { + login, + modelAvatars, + modelPricingLink, + planModels, + studentModels, + supportModels, +} from "@/conf.ts"; import { splitList } from "@/utils/base.ts"; import { Model } from "@/conversation/types.ts"; -import {useDispatch, useSelector} from "react-redux"; -import {addModelList, closeMarket, removeModelList, selectModel, selectModelList, setModel} from "@/store/chat.ts"; -import {Button} from "@/components/ui/button.tsx"; -import {isSubscribedSelector} from "@/store/subscription.ts"; -import {teenagerSelector} from "@/store/package.ts"; +import { useDispatch, useSelector } from "react-redux"; +import { + addModelList, + closeMarket, + removeModelList, + selectModel, + selectModelList, + setModel, +} from "@/store/chat.ts"; +import { Button } from "@/components/ui/button.tsx"; +import { isSubscribedSelector } from "@/store/subscription.ts"; +import { teenagerSelector } from "@/store/package.ts"; +import { ToastAction } from "@/components/ui/toast.tsx"; +import { selectAuthenticated } from "@/store/auth.ts"; +import { useToast } from "@/components/ui/use-toast.ts"; type SearchBarProps = { value: string; @@ -46,11 +71,13 @@ type ModelProps = { function ModelItem({ model, className, style }: ModelProps) { const { t } = useTranslation(); const dispatch = useDispatch(); + const { toast } = useToast(); const list = useSelector(selectModelList); const current = useSelector(selectModel); const subscription = useSelector(isSubscribedSelector); const student = useSelector(teenagerSelector); + const auth = useSelector(selectAuthenticated); const state = useMemo(() => { if (current === model.id) return 0; @@ -59,9 +86,10 @@ function ModelItem({ model, className, style }: ModelProps) { }, [model, current, list]); const pro = useMemo(() => { - if (subscription && planModels.includes(model.id)) return true; - if (student && studentModels.includes(model.id)) return true; - return false; + return ( + (subscription && planModels.includes(model.id)) || + (student && studentModels.includes(model.id)) + ); }, [model, subscription, student]); const avatar = useMemo(() => { @@ -70,14 +98,31 @@ function ModelItem({ model, className, style }: ModelProps) { }, [model]); return ( -
{ - dispatch(addModelList(model.id)); - dispatch(setModel(model.id)); - dispatch(closeMarket()); - }}> +
{ + dispatch(addModelList(model.id)); + + if (!auth && model.auth) { + toast({ + title: t("login-require"), + action: ( + + {t("login")} + + ), + }); + return; + } + + dispatch(setModel(model.id)); + dispatch(closeMarket()); + }} + > {model.name}
-

{model.name}

+

{model.name}

{model.tag && model.tag.map((tag, index) => { @@ -91,25 +136,26 @@ function ModelItem({ model, className, style }: ModelProps) {
-
@@ -163,16 +209,21 @@ function MarketHeader() { return (
-

{t("market.explore")}

- ) + ); } function MarketFooter() { diff --git a/app/src/components/home/SideBar.tsx b/app/src/components/home/SideBar.tsx index 10b9df3..58721e6 100644 --- a/app/src/components/home/SideBar.tsx +++ b/app/src/components/home/SideBar.tsx @@ -1,7 +1,7 @@ import { useTranslation } from "react-i18next"; import { useDispatch, useSelector } from "react-redux"; import { selectAuthenticated, selectUsername } from "@/store/auth.ts"; -import {closeMarket, selectCurrent, selectHistory} from "@/store/chat.ts"; +import { closeMarket, selectCurrent, selectHistory } from "@/store/chat.ts"; import { useRef, useState } from "react"; import { ConversationInstance } from "@/conversation/types.ts"; import { useToast } from "@/components/ui/use-toast.ts"; diff --git a/app/src/conf.ts b/app/src/conf.ts index 1a4ae01..6c459aa 100644 --- a/app/src/conf.ts +++ b/app/src/conf.ts @@ -36,7 +36,7 @@ export const supportModels: Model[] = [ id: "gpt-3.5-turbo-1106", name: "GPT-3.5 1106", free: true, - auth: false, + auth: true, tag: ["free", "official"], }, { @@ -324,6 +324,7 @@ export const planModels = [ "gpt-4-dalle", "claude-2", "claude-2-100k", + "midjourney-fast", ]; export const expensiveModels = [ diff --git a/app/src/conversation/addition.ts b/app/src/conversation/addition.ts index 23f6508..0d991c2 100644 --- a/app/src/conversation/addition.ts +++ b/app/src/conversation/addition.ts @@ -16,10 +16,7 @@ type SubscriptionResponse = { is_subscribed: boolean; expired: number; enterprise?: boolean; - usage: { - gpt4: number; - claude100k: number; - }; + usage: Record; }; type BuySubscriptionResponse = { @@ -67,7 +64,7 @@ export async function getSubscription(): Promise { status: false, is_subscribed: false, expired: 0, - usage: { gpt4: 0, claude100k: 0 }, + usage: {}, }; } return resp.data as SubscriptionResponse; @@ -77,7 +74,7 @@ export async function getSubscription(): Promise { status: false, is_subscribed: false, expired: 0, - usage: { gpt4: 0, claude100k: 0 }, + usage: {}, }; } } diff --git a/app/src/dialogs/Subscription.tsx b/app/src/dialogs/Subscription.tsx index 745791d..9f52b24 100644 --- a/app/src/dialogs/Subscription.tsx +++ b/app/src/dialogs/Subscription.tsx @@ -30,6 +30,7 @@ import { FolderGit2, Globe, Image, + ImagePlus, LifeBuoy, MessageSquare, MessagesSquare, @@ -199,12 +200,20 @@ function Subscription() {
{!enterprise && ( <> +
+ + Midjourney +
+
+

{usage?.midjourney || 0}

/

10

+
+
GPT-4
-

{usage?.gpt4}

/

100

+

{usage?.gpt4 || 0}

/

100

@@ -212,7 +221,7 @@ function Subscription() { Claude 100k
-

{usage?.claude100k}

/

100

+

{usage?.claude100k || 0}

/

100

@@ -262,6 +271,10 @@ function Subscription() { {t("sub.pro-claude")}
+
+ + {t("sub.pro-mj")} +
{t("sub.pro-service")} diff --git a/app/src/i18n.ts b/app/src/i18n.ts index cd6ed0a..7aace30 100644 --- a/app/src/i18n.ts +++ b/app/src/i18n.ts @@ -130,6 +130,7 @@ const resources = { "free-web": "联网搜索功能", "free-conversation": "对话存储记录", "free-api": "API 调用", + "pro-mj": "Midjourney 每日免费绘图 10 次", "pro-gpt4": "GPT-4 每日请求 100 次", "pro-gpt4-desc": "(包含 GPT 4 Turbo, GPT 4V, GPT 4 DALLE)", "pro-claude": "Claude 100k 每日请求 100 次", @@ -337,6 +338,25 @@ const resources = { pricing: "See model pricing for more details", true: "Yes", false: "No", + tag: { + free: "Free", + official: "Official", + unstable: "Unstable", + web: "Web", + "high-quality": "High Quality", + "high-context": "High Context", + "high-price": "High Price", + "open-source": "Open Source", + "image-generation": "Image Generation", + "multi-modal": "Multi Modal", + fast: "Fast", + "english-model": "English Model", + }, + market: { + model: "Explore more models", + explore: "Explore", + search: "Search model name or description", + }, conversation: { title: "Conversation", empty: "Empty", @@ -423,6 +443,7 @@ const resources = { "free-web": "web searching feature", "free-conversation": "conversation storage", "free-api": "API calls", + "pro-mj": "Midjourney 10 times free image generation per day", "pro-gpt4": "GPT-4 100 requests per day", "pro-gpt4-desc": "(including GPT 4 Turbo, GPT 4V, GPT 4 DALLE)", "pro-claude": "Claude 100k 100 requests per day", @@ -641,6 +662,20 @@ const resources = { "См. ценообразование моделей для получения дополнительной информации", true: "Да", false: "Нет", + tag: { + free: "Бесплатно", + official: "Официальный", + unstable: "Нестабильный", + web: "Веб", + "high-quality": "Высокое качество", + "high-context": "Высокий контекст", + "high-price": "Высокая цена", + "open-source": "Открытый исходный код", + "image-generation": "Генерация изображений", + "multi-modal": "Мульти Модальный", + fast: "Быстрый", + "english-model": "Английская модель", + }, conversation: { title: "Разговор", empty: "Пусто", @@ -728,6 +763,7 @@ const resources = { "free-web": "веб-поиск", "free-conversation": "хранение разговоров", "free-api": "API вызовы", + "pro-mj": "Midjourney 10 раз бесплатно генерировать изображения в день", "pro-gpt4": "GPT-4 100 запросов в день", "pro-gpt4-desc": "(включая GPT 4 Turbo, GPT 4V, GPT 4 DALLE)", "pro-claude": "Claude 100k 100 запросов в день", diff --git a/auth/rule.go b/auth/rule.go index a784d83..52d3d1c 100644 --- a/auth/rule.go +++ b/auth/rule.go @@ -41,13 +41,14 @@ func HandleSubscriptionUsage(db *sql.DB, cache *redis.Client, user *User, model if globals.IsGPT3TurboModel(model) { // independent channel for subscription users return subscription - } - if globals.IsGPT4NativeModel(model) { + } else if globals.IsGPT4NativeModel(model) { return subscription && IncreaseSubscriptionUsage(cache, user, globals.GPT4, 100) } else if globals.IsClaude100KModel(model) { if subscription || user.HasTeenagerPackage(db) { return IncreaseSubscriptionUsage(cache, user, globals.Claude2100k, 100) } + } else if model == globals.MidjourneyFast { + return subscription && IncreaseSubscriptionUsage(cache, user, globals.MidjourneyFast, 10) } return false diff --git a/auth/user.go b/auth/user.go index a087fda..dbc89ad 100644 --- a/auth/user.go +++ b/auth/user.go @@ -204,15 +204,13 @@ func (u *User) GetSubscriptionExpiredDay(db *sql.DB) int { return int(math.Round(stamp.Hours() / 24)) } -type Usage struct { - GPT4 int64 `json:"gpt4"` - Claude100k int64 `json:"claude100k"` -} +type Usage map[string]int64 func (u *User) GetSubscriptionUsage(db *sql.DB, cache *redis.Client) Usage { return Usage{ - GPT4: utils.MustInt(cache, globals.GetSubscriptionLimitFormat(globals.GPT4, u.GetID(db))), - Claude100k: utils.MustInt(cache, globals.GetSubscriptionLimitFormat(globals.Claude2100k, u.GetID(db))), + "gpt4": utils.MustInt(cache, globals.GetSubscriptionLimitFormat(globals.GPT4, u.GetID(db))), + "claude100k": utils.MustInt(cache, globals.GetSubscriptionLimitFormat(globals.Claude2100k, u.GetID(db))), + "midjourney": utils.MustInt(cache, globals.GetSubscriptionLimitFormat(globals.MidjourneyFast, u.GetID(db))), } }