mirror of
https://github.com/coaidev/coai.git
synced 2025-05-19 21:10:18 +09:00
feat: update subscription life cycle
This commit is contained in:
parent
dc5c6edcfd
commit
f8af7079d8
@ -1,5 +1,4 @@
|
|||||||
import { Conversation } from "./conversation.ts";
|
import { Conversation } from "./conversation.ts";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export type Message = {
|
export type Message = {
|
||||||
role: string;
|
role: string;
|
||||||
@ -49,11 +48,3 @@ export type Plan = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Plans = Plan[];
|
export type Plans = Plan[];
|
||||||
|
|
||||||
export type SubscriptionUsage = Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
icon: React.ReactElement;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { Model } from "@/api/types.ts";
|
import { Model, Plan } from "@/api/types.ts";
|
||||||
import { ChargeProps } from "@/admin/charge.ts";
|
import { ChargeProps } from "@/admin/charge.ts";
|
||||||
|
|
||||||
export async function getApiModels(): Promise<string[]> {
|
export async function getApiModels(): Promise<string[]> {
|
||||||
@ -12,6 +12,16 @@ export async function getApiModels(): Promise<string[]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getApiPlans(): Promise<Plan[]> {
|
||||||
|
try {
|
||||||
|
const res = await axios.get("/v1/plans");
|
||||||
|
return res.data as Plan[];
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function getApiMarket(): Promise<Model[]> {
|
export async function getApiMarket(): Promise<Model[]> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get("/v1/market");
|
const res = await axios.get("/v1/market");
|
||||||
|
@ -3,14 +3,19 @@ import { ThemeProvider } from "@/components/ThemeProvider.tsx";
|
|||||||
import DialogManager from "@/dialogs";
|
import DialogManager from "@/dialogs";
|
||||||
import Broadcast from "@/components/Broadcast.tsx";
|
import Broadcast from "@/components/Broadcast.tsx";
|
||||||
import { useEffectAsync } from "@/utils/hook.ts";
|
import { useEffectAsync } from "@/utils/hook.ts";
|
||||||
import { allModels, supportModels } from "@/conf";
|
import { allModels, subscriptionData, supportModels } from "@/conf";
|
||||||
import { channelModels } from "@/admin/channel.ts";
|
import { channelModels } from "@/admin/channel.ts";
|
||||||
import { getApiCharge, getApiMarket, getApiModels } from "@/api/v1.ts";
|
import {
|
||||||
|
getApiCharge,
|
||||||
|
getApiMarket,
|
||||||
|
getApiModels,
|
||||||
|
getApiPlans,
|
||||||
|
} from "@/api/v1.ts";
|
||||||
import { loadPreferenceModels } from "@/utils/storage.ts";
|
import { loadPreferenceModels } from "@/utils/storage.ts";
|
||||||
import { resetJsArray } from "@/utils/base.ts";
|
import { resetJsArray } from "@/utils/base.ts";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { initChatModels } from "@/store/chat.ts";
|
import { initChatModels } from "@/store/chat.ts";
|
||||||
import { Model } from "@/api/types.ts";
|
import { Model, Plan } from "@/api/types.ts";
|
||||||
import { ChargeProps, nonBilling } from "@/admin/charge.ts";
|
import { ChargeProps, nonBilling } from "@/admin/charge.ts";
|
||||||
|
|
||||||
function AppProvider() {
|
function AppProvider() {
|
||||||
@ -40,6 +45,12 @@ function AppProvider() {
|
|||||||
if (!allModels.includes(model)) allModels.push(model);
|
if (!allModels.includes(model)) allModels.push(model);
|
||||||
if (!channelModels.includes(model)) channelModels.push(model);
|
if (!channelModels.includes(model)) channelModels.push(model);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const plans = await getApiPlans();
|
||||||
|
resetJsArray(
|
||||||
|
subscriptionData,
|
||||||
|
plans.filter((plan: Plan) => plan.level !== 0),
|
||||||
|
);
|
||||||
}, [allModels]);
|
}, [allModels]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -29,6 +29,7 @@ import { openDialog as openApiDialog } from "@/store/api.ts";
|
|||||||
import router from "@/router.tsx";
|
import router from "@/router.tsx";
|
||||||
import { useDeeptrain } from "@/conf/env.ts";
|
import { useDeeptrain } from "@/conf/env.ts";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { subscriptionData } from "@/conf";
|
||||||
|
|
||||||
type MenuBarProps = {
|
type MenuBarProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -61,10 +62,12 @@ function MenuBar({ children, className }: MenuBarProps) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
{subscriptionData.length > 0 && (
|
||||||
<DropdownMenuItem onClick={() => dispatch(openSub())}>
|
<DropdownMenuItem onClick={() => dispatch(openSub())}>
|
||||||
<CalendarPlus className={`h-4 w-4 mr-1`} />
|
<CalendarPlus className={`h-4 w-4 mr-1`} />
|
||||||
{t("sub.title")}
|
{t("sub.title")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
{useDeeptrain && (
|
{useDeeptrain && (
|
||||||
<DropdownMenuItem onClick={() => dispatch(openPackageDialog())}>
|
<DropdownMenuItem onClick={() => dispatch(openPackageDialog())}>
|
||||||
<Boxes className={`h-4 w-4 mr-1`} />
|
<Boxes className={`h-4 w-4 mr-1`} />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import SelectGroup, { SelectItemProps } from "@/components/SelectGroup.tsx";
|
import SelectGroup, { SelectItemProps } from "@/components/SelectGroup.tsx";
|
||||||
import { supportModels } from "@/conf";
|
import { subscriptionData, supportModels } from "@/conf";
|
||||||
import {
|
import {
|
||||||
openMarket,
|
openMarket,
|
||||||
selectModel,
|
selectModel,
|
||||||
@ -84,7 +84,7 @@ function ModelFinder(props: ModelSelectorProps) {
|
|||||||
value: t("market.model"),
|
value: t("market.model"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [supportModels, level, student, sync]);
|
}, [supportModels, subscriptionData, level, student, sync]);
|
||||||
|
|
||||||
const current = useMemo((): SelectItemProps => {
|
const current = useMemo((): SelectItemProps => {
|
||||||
const raw = models.find((item) => item.name === model);
|
const raw = models.find((item) => item.name === model);
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { supportModels } from "@/conf";
|
import { subscriptionData, supportModels } from "@/conf";
|
||||||
import { isUrl, splitList } from "@/utils/base.ts";
|
import { isUrl, splitList } from "@/utils/base.ts";
|
||||||
import { Model } from "@/api/types.ts";
|
import { Model } from "@/api/types.ts";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
@ -123,7 +123,7 @@ function ModelItem({
|
|||||||
|
|
||||||
const pro = useMemo(() => {
|
const pro = useMemo(() => {
|
||||||
return includingModelFromPlan(level, model.id);
|
return includingModelFromPlan(level, model.id);
|
||||||
}, [model, level, student]);
|
}, [subscriptionData, model, level, student]);
|
||||||
|
|
||||||
const avatar = useMemo(() => {
|
const avatar = useMemo(() => {
|
||||||
return isUrl(model.avatar) ? model.avatar : `/icons/${model.avatar}`;
|
return isUrl(model.avatar) ? model.avatar : `/icons/${model.avatar}`;
|
||||||
|
@ -12,110 +12,15 @@ import { setAxiosConfig } from "@/conf/api.ts";
|
|||||||
export const version = "3.8.6"; // version of the current build
|
export const version = "3.8.6"; // version of the current build
|
||||||
export const dev: boolean = getDev(); // is in development mode (for debugging, in localhost origin)
|
export const dev: boolean = getDev(); // is in development mode (for debugging, in localhost origin)
|
||||||
export const deploy: boolean = true; // is production environment (for api endpoint)
|
export const deploy: boolean = true; // is production environment (for api endpoint)
|
||||||
|
export const tokenField = getTokenField(deploy); // token field name for storing token
|
||||||
|
|
||||||
export let apiEndpoint: string = getRestApi(deploy); // api endpoint for rest api calls
|
export let apiEndpoint: string = getRestApi(deploy); // api endpoint for rest api calls
|
||||||
export let websocketEndpoint: string = getWebsocketApi(deploy); // api endpoint for websocket calls
|
export let websocketEndpoint: string = getWebsocketApi(deploy); // api endpoint for websocket calls
|
||||||
export const tokenField = getTokenField(deploy); // token field name for storing token
|
|
||||||
|
|
||||||
export let supportModels: Model[] = loadPreferenceModels(getOfflineModels()); // support models in model market of the current site
|
export let supportModels: Model[] = loadPreferenceModels(getOfflineModels()); // support models in model market of the current site
|
||||||
export let allModels: string[] = supportModels.map((model) => model.id); // all support model id list of the current site
|
export let allModels: string[] = supportModels.map((model) => model.id); // all support model id list of the current site
|
||||||
|
|
||||||
const GPT4Array = [
|
export let subscriptionData: Plans = []; // subscription data of the current site
|
||||||
"gpt-4",
|
|
||||||
"gpt-4-0314",
|
|
||||||
"gpt-4-0613",
|
|
||||||
"gpt-4-1106-preview",
|
|
||||||
"gpt-4-vision-preview",
|
|
||||||
"gpt-4-v",
|
|
||||||
"gpt-4-dalle",
|
|
||||||
"gpt-4-all",
|
|
||||||
];
|
|
||||||
const Claude100kArray = ["claude-1.3", "claude-2", "claude-2.1"];
|
|
||||||
const MidjourneyArray = ["midjourney-fast"];
|
|
||||||
|
|
||||||
export const subscriptionData: Plans = [
|
|
||||||
{
|
|
||||||
level: 1,
|
|
||||||
price: 42,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: "gpt-4",
|
|
||||||
icon: "compass",
|
|
||||||
name: "GPT-4",
|
|
||||||
value: 150,
|
|
||||||
models: GPT4Array,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "midjourney",
|
|
||||||
icon: "image-plus",
|
|
||||||
name: "Midjourney",
|
|
||||||
value: 50,
|
|
||||||
models: MidjourneyArray,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "claude-100k",
|
|
||||||
icon: "book-text",
|
|
||||||
name: "Claude 100k",
|
|
||||||
value: 300,
|
|
||||||
models: Claude100kArray,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
level: 2,
|
|
||||||
price: 76,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: "gpt-4",
|
|
||||||
icon: "compass",
|
|
||||||
name: "GPT-4",
|
|
||||||
value: 300,
|
|
||||||
models: GPT4Array,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "midjourney",
|
|
||||||
icon: "image-plus",
|
|
||||||
name: "Midjourney",
|
|
||||||
value: 100,
|
|
||||||
models: MidjourneyArray,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "claude-100k",
|
|
||||||
icon: "book-text",
|
|
||||||
name: "Claude 100k",
|
|
||||||
value: 600,
|
|
||||||
models: Claude100kArray,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
level: 3,
|
|
||||||
price: 148,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: "gpt-4",
|
|
||||||
icon: "compass",
|
|
||||||
name: "GPT-4",
|
|
||||||
value: 600,
|
|
||||||
models: GPT4Array,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "midjourney",
|
|
||||||
icon: "image-plus",
|
|
||||||
name: "Midjourney",
|
|
||||||
value: 200,
|
|
||||||
models: MidjourneyArray,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "claude-100k",
|
|
||||||
icon: "book-text",
|
|
||||||
name: "Claude 100k",
|
|
||||||
value: 1200,
|
|
||||||
models: Claude100kArray,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
setAxiosConfig({
|
setAxiosConfig({
|
||||||
endpoint: apiEndpoint,
|
endpoint: apiEndpoint,
|
||||||
|
@ -41,7 +41,11 @@ export function SubscriptionIcon({ type, className }: SubscriptionIconProps) {
|
|||||||
|
|
||||||
export function getPlan(level: number): Plan {
|
export function getPlan(level: number): Plan {
|
||||||
const raw = subscriptionData.filter((item) => item.level === level);
|
const raw = subscriptionData.filter((item) => item.level === level);
|
||||||
return raw.length > 0 ? raw[0] : subscriptionData[0];
|
return raw.length > 0
|
||||||
|
? raw[0]
|
||||||
|
: subscriptionData.length
|
||||||
|
? subscriptionData[0]
|
||||||
|
: { level: 0, price: 0, items: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPlanModels(level: number): string[] {
|
export function getPlanModels(level: number): string[] {
|
||||||
|
@ -42,6 +42,7 @@ import { ToastAction } from "@/components/ui/toast.tsx";
|
|||||||
import { deeptrainEndpoint, docsEndpoint, useDeeptrain } from "@/conf/env.ts";
|
import { deeptrainEndpoint, docsEndpoint, useDeeptrain } from "@/conf/env.ts";
|
||||||
import { useRedeem } from "@/api/redeem.ts";
|
import { useRedeem } from "@/api/redeem.ts";
|
||||||
import { cn } from "@/components/ui/lib/utils.ts";
|
import { cn } from "@/components/ui/lib/utils.ts";
|
||||||
|
import { subscriptionData } from "@/conf";
|
||||||
|
|
||||||
type AmountComponentProps = {
|
type AmountComponentProps = {
|
||||||
amount: number;
|
amount: number;
|
||||||
@ -105,6 +106,7 @@ function QuotaDialog() {
|
|||||||
<DialogTitle>{t("buy.choose")}</DialogTitle>
|
<DialogTitle>{t("buy.choose")}</DialogTitle>
|
||||||
<DialogDescription asChild>
|
<DialogDescription asChild>
|
||||||
<div className={`dialog-wrapper`}>
|
<div className={`dialog-wrapper`}>
|
||||||
|
{subscriptionData.length > 0 && (
|
||||||
<p
|
<p
|
||||||
className={`link translate-y-2 text-center`}
|
className={`link translate-y-2 text-center`}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@ -113,6 +115,7 @@ function QuotaDialog() {
|
|||||||
>
|
>
|
||||||
{t("sub.subscription-link")}
|
{t("sub.subscription-link")}
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
<div className={`buy-interface`}>
|
<div className={`buy-interface`}>
|
||||||
<div className={`interface-item`}>
|
<div className={`interface-item`}>
|
||||||
<div className={`amount-container`}>
|
<div className={`amount-container`}>
|
||||||
|
@ -46,8 +46,8 @@ type PlanItemProps = {
|
|||||||
function PlanItem({ level }: PlanItemProps) {
|
function PlanItem({ level }: PlanItemProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const current = useSelector(levelSelector);
|
const current = useSelector(levelSelector);
|
||||||
const plan = useMemo(() => getPlan(level), [level]);
|
const plan = useMemo(() => getPlan(level), [subscriptionData, level]);
|
||||||
const name = useMemo(() => getPlanName(level), [level]);
|
const name = useMemo(() => getPlanName(level), [subscriptionData, level]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("plan", name)}>
|
<div className={cn("plan", name)}>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
import { getSubscription } from "@/api/addition.ts";
|
import { getSubscription } from "@/api/addition.ts";
|
||||||
import { AppDispatch } from "./index.ts";
|
import { AppDispatch } from "./index.ts";
|
||||||
|
import { subscriptionData } from "@/conf";
|
||||||
|
|
||||||
export const subscriptionSlice = createSlice({
|
export const subscriptionSlice = createSlice({
|
||||||
name: "subscription",
|
name: "subscription",
|
||||||
@ -16,12 +17,14 @@ export const subscriptionSlice = createSlice({
|
|||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
toggleDialog: (state) => {
|
toggleDialog: (state) => {
|
||||||
|
if (!state.dialog && !subscriptionData.length) return;
|
||||||
state.dialog = !state.dialog;
|
state.dialog = !state.dialog;
|
||||||
},
|
},
|
||||||
setDialog: (state, action) => {
|
setDialog: (state, action) => {
|
||||||
state.dialog = action.payload as boolean;
|
state.dialog = action.payload as boolean;
|
||||||
},
|
},
|
||||||
openDialog: (state) => {
|
openDialog: (state) => {
|
||||||
|
if (!subscriptionData.length) return;
|
||||||
state.dialog = true;
|
state.dialog = true;
|
||||||
},
|
},
|
||||||
closeDialog: (state) => {
|
closeDialog: (state) => {
|
||||||
|
85
auth/plan.go
85
auth/plan.go
@ -12,54 +12,69 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Plan struct {
|
type Plan struct {
|
||||||
Level int
|
Level int `json:"level" mapstructure:"level"`
|
||||||
Price float32
|
Price float32 `json:"price" mapstructure:"price"`
|
||||||
Usage []PlanUsage
|
Items []PlanItem `json:"items" mapstructure:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlanUsage struct {
|
type PlanItem struct {
|
||||||
Id string
|
Id string `json:"id" mapstructure:"id"`
|
||||||
Value int64
|
Name string `json:"name" mapstructure:"name"`
|
||||||
Including func(string) bool
|
Icon string `json:"icon" mapstructure:"icon"`
|
||||||
|
Value int64 `json:"value" mapstructure:"value"`
|
||||||
|
Models []string `json:"models" mapstructure:"models"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Usage struct {
|
type Usage struct {
|
||||||
Used int64 `json:"used"`
|
Used int64 `json:"used" mapstructure:"used"`
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total" mapstructure:"total"`
|
||||||
}
|
}
|
||||||
type UsageMap map[string]Usage
|
type UsageMap map[string]Usage
|
||||||
|
|
||||||
|
var GPT4Array = []string{
|
||||||
|
globals.GPT4, globals.GPT40314, globals.GPT40613, globals.GPT41106Preview, globals.GPT41106VisionPreview,
|
||||||
|
globals.GPT4Vision, globals.GPT4Dalle, globals.GPT4All,
|
||||||
|
}
|
||||||
|
|
||||||
|
var ClaudeProArray = []string{
|
||||||
|
globals.Claude1100k, globals.Claude2100k, globals.Claude2200k,
|
||||||
|
}
|
||||||
|
|
||||||
|
var MidjourneyArray = []string{
|
||||||
|
globals.MidjourneyFast,
|
||||||
|
}
|
||||||
|
|
||||||
var Plans = []Plan{
|
var Plans = []Plan{
|
||||||
{
|
{
|
||||||
Level: 0,
|
Level: 0,
|
||||||
Price: 0,
|
Price: 0,
|
||||||
Usage: []PlanUsage{},
|
Items: []PlanItem{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Level: 1,
|
Level: 1,
|
||||||
Price: 42,
|
Price: 42,
|
||||||
Usage: []PlanUsage{
|
Items: []PlanItem{
|
||||||
{Id: "gpt-4", Value: 150, Including: globals.IsGPT4NativeModel},
|
{Id: "gpt-4", Value: 150, Models: GPT4Array, Name: "GPT-4", Icon: "compass"},
|
||||||
{Id: "claude-100k", Value: 300, Including: globals.IsClaude100KModel},
|
{Id: "midjourney", Value: 50, Models: MidjourneyArray, Name: "Midjourney", Icon: "image-plus"},
|
||||||
{Id: "midjourney", Value: 50, Including: globals.IsMidjourneyFastModel},
|
{Id: "claude-100k", Value: 300, Models: ClaudeProArray, Name: "Claude 100k", Icon: "book-text"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Level: 2,
|
Level: 2,
|
||||||
Price: 76,
|
Price: 76,
|
||||||
Usage: []PlanUsage{
|
Items: []PlanItem{
|
||||||
{Id: "gpt-4", Value: 300, Including: globals.IsGPT4NativeModel},
|
{Id: "gpt-4", Value: 300, Models: GPT4Array, Name: "GPT-4", Icon: "compass"},
|
||||||
{Id: "claude-100k", Value: 600, Including: globals.IsClaude100KModel},
|
{Id: "midjourney", Value: 100, Models: MidjourneyArray, Name: "Midjourney", Icon: "image-plus"},
|
||||||
{Id: "midjourney", Value: 100, Including: globals.IsMidjourneyFastModel},
|
{Id: "claude-100k", Value: 600, Models: ClaudeProArray, Name: "Claude 100k", Icon: "book-text"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Level: 3,
|
Level: 3,
|
||||||
Price: 148,
|
Price: 148,
|
||||||
Usage: []PlanUsage{
|
Items: []PlanItem{
|
||||||
{Id: "gpt-4", Value: 600, Including: globals.IsGPT4NativeModel},
|
{Id: "gpt-4", Value: 600, Models: GPT4Array, Name: "GPT-4", Icon: "compass"},
|
||||||
{Id: "claude-100k", Value: 1200, Including: globals.IsClaude100KModel},
|
{Id: "midjourney", Value: 200, Models: MidjourneyArray, Name: "Midjourney", Icon: "image-plus"},
|
||||||
{Id: "midjourney", Value: 200, Including: globals.IsMidjourneyFastModel},
|
{Id: "claude-100k", Value: 1200, Models: ClaudeProArray, Name: "Claude 100k", Icon: "book-text"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -155,19 +170,19 @@ func DecreaseSubscriptionUsage(cache *redis.Client, user *User, t string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Plan) GetUsage(user *User, db *sql.DB, cache *redis.Client) UsageMap {
|
func (p *Plan) GetUsage(user *User, db *sql.DB, cache *redis.Client) UsageMap {
|
||||||
return utils.EachObject[PlanUsage, Usage](p.Usage, func(usage PlanUsage) (string, Usage) {
|
return utils.EachObject[PlanItem, Usage](p.Items, func(usage PlanItem) (string, Usage) {
|
||||||
return usage.Id, usage.GetUsageForm(user, db, cache)
|
return usage.Id, usage.GetUsageForm(user, db, cache)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlanUsage) GetUsage(user *User, db *sql.DB, cache *redis.Client) int64 {
|
func (p *PlanItem) GetUsage(user *User, db *sql.DB, cache *redis.Client) int64 {
|
||||||
// preflight check
|
// preflight check
|
||||||
user.GetID(db)
|
user.GetID(db)
|
||||||
usage, _ := GetSubscriptionUsage(cache, user, p.Id)
|
usage, _ := GetSubscriptionUsage(cache, user, p.Id)
|
||||||
return usage
|
return usage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlanUsage) ResetUsage(user *User, cache *redis.Client) bool {
|
func (p *PlanItem) ResetUsage(user *User, cache *redis.Client) bool {
|
||||||
key := globals.GetSubscriptionLimitFormat(p.Id, user.ID)
|
key := globals.GetSubscriptionLimitFormat(p.Id, user.ID)
|
||||||
_, offset := GetSubscriptionUsage(cache, user, p.Id)
|
_, offset := GetSubscriptionUsage(cache, user, p.Id)
|
||||||
|
|
||||||
@ -175,36 +190,36 @@ func (p *PlanUsage) ResetUsage(user *User, cache *redis.Client) bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlanUsage) CreateUsage(user *User, cache *redis.Client) bool {
|
func (p *PlanItem) CreateUsage(user *User, cache *redis.Client) bool {
|
||||||
key := globals.GetSubscriptionLimitFormat(p.Id, user.ID)
|
key := globals.GetSubscriptionLimitFormat(p.Id, user.ID)
|
||||||
|
|
||||||
err := utils.SetCache(cache, key, getOffsetFormat(time.Now(), 0), planExp)
|
err := utils.SetCache(cache, key, getOffsetFormat(time.Now(), 0), planExp)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlanUsage) GetUsageForm(user *User, db *sql.DB, cache *redis.Client) Usage {
|
func (p *PlanItem) GetUsageForm(user *User, db *sql.DB, cache *redis.Client) Usage {
|
||||||
return Usage{
|
return Usage{
|
||||||
Used: p.GetUsage(user, db, cache),
|
Used: p.GetUsage(user, db, cache),
|
||||||
Total: p.Value,
|
Total: p.Value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlanUsage) IsInfinity() bool {
|
func (p *PlanItem) IsInfinity() bool {
|
||||||
return p.Value == -1
|
return p.Value == -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlanUsage) IsExceeded(user *User, db *sql.DB, cache *redis.Client) bool {
|
func (p *PlanItem) IsExceeded(user *User, db *sql.DB, cache *redis.Client) bool {
|
||||||
return p.IsInfinity() || p.GetUsage(user, db, cache) < p.Value
|
return p.IsInfinity() || p.GetUsage(user, db, cache) < p.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlanUsage) Increase(user *User, cache *redis.Client) bool {
|
func (p *PlanItem) Increase(user *User, cache *redis.Client) bool {
|
||||||
if p.Value == -1 {
|
if p.Value == -1 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return IncreaseSubscriptionUsage(cache, user, p.Id, p.Value)
|
return IncreaseSubscriptionUsage(cache, user, p.Id, p.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlanUsage) Decrease(user *User, cache *redis.Client) bool {
|
func (p *PlanItem) Decrease(user *User, cache *redis.Client) bool {
|
||||||
if p.Value == -1 {
|
if p.Value == -1 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -217,8 +232,8 @@ func (u *User) GetSubscriptionUsage(db *sql.DB, cache *redis.Client) UsageMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Plan) IncreaseUsage(user *User, cache *redis.Client, model string) bool {
|
func (p *Plan) IncreaseUsage(user *User, cache *redis.Client, model string) bool {
|
||||||
for _, usage := range p.Usage {
|
for _, usage := range p.Items {
|
||||||
if usage.Including(model) {
|
if utils.Contains(model, usage.Models) {
|
||||||
return usage.Increase(user, cache)
|
return usage.Increase(user, cache)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,8 +242,8 @@ func (p *Plan) IncreaseUsage(user *User, cache *redis.Client, model string) bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Plan) DecreaseUsage(user *User, cache *redis.Client, model string) bool {
|
func (p *Plan) DecreaseUsage(user *User, cache *redis.Client, model string) bool {
|
||||||
for _, usage := range p.Usage {
|
for _, usage := range p.Items {
|
||||||
if usage.Including(model) {
|
if utils.Contains(model, usage.Models) {
|
||||||
return usage.Decrease(user, cache)
|
return usage.Decrease(user, cache)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ func BuySubscription(db *sql.DB, cache *redis.Client, user *User, level int, mon
|
|||||||
// new subscription
|
// new subscription
|
||||||
|
|
||||||
plan := user.GetPlan(db)
|
plan := user.GetPlan(db)
|
||||||
for _, usage := range plan.Usage {
|
for _, usage := range plan.Items {
|
||||||
// create usage
|
// create usage
|
||||||
usage.CreateUsage(user, cache)
|
usage.CreateUsage(user, cache)
|
||||||
}
|
}
|
||||||
|
@ -97,9 +97,8 @@ const (
|
|||||||
SkylarkChat = "skylark-chat"
|
SkylarkChat = "skylark-chat"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GPT4Array = []string{
|
var DalleModels = []string{
|
||||||
GPT4, GPT40314, GPT40613, GPT41106Preview, GPT41106VisionPreview,
|
Dalle, Dalle2, Dalle3,
|
||||||
GPT4Vision, GPT4Dalle, GPT4All,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func in(value string, slice []string) bool {
|
func in(value string, slice []string) bool {
|
||||||
@ -111,22 +110,13 @@ func in(value string, slice []string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsGPT4NativeModel(model string) bool {
|
|
||||||
return in(model, GPT4Array)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsDalleModel(model string) bool {
|
func IsDalleModel(model string) bool {
|
||||||
return model == Dalle || model == Dalle2 || model == Dalle3
|
// using image generation api if model is in dalle models
|
||||||
}
|
return in(model, DalleModels)
|
||||||
|
|
||||||
func IsClaude100KModel(model string) bool {
|
|
||||||
return model == Claude1100k || model == Claude2100k || model == Claude2200k
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsMidjourneyFastModel(model string) bool {
|
|
||||||
return model == MidjourneyFast
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsGPT41106VisionPreview(model string) bool {
|
func IsGPT41106VisionPreview(model string) bool {
|
||||||
return model == GPT41106VisionPreview || strings.Contains(model, GPT41106VisionPreview)
|
// enable openai image format for gpt-4-vision-preview model
|
||||||
|
return model == GPT41106VisionPreview ||
|
||||||
|
strings.Contains(model, GPT41106VisionPreview)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package manager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"chat/admin"
|
"chat/admin"
|
||||||
|
"chat/auth"
|
||||||
"chat/channel"
|
"chat/channel"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -19,6 +20,10 @@ func ChargeAPI(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, channel.ChargeInstance.ListRules())
|
c.JSON(http.StatusOK, channel.ChargeInstance.ListRules())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PlanAPI(c *gin.Context) {
|
||||||
|
c.JSON(http.StatusOK, auth.Plans)
|
||||||
|
}
|
||||||
|
|
||||||
func sendErrorResponse(c *gin.Context, err error, types ...string) {
|
func sendErrorResponse(c *gin.Context, err error, types ...string) {
|
||||||
var errType string
|
var errType string
|
||||||
if len(types) > 0 {
|
if len(types) > 0 {
|
||||||
|
@ -10,6 +10,7 @@ func Register(app *gin.RouterGroup) {
|
|||||||
app.GET("/v1/models", ModelAPI)
|
app.GET("/v1/models", ModelAPI)
|
||||||
app.GET("/v1/market", MarketAPI)
|
app.GET("/v1/market", MarketAPI)
|
||||||
app.GET("/v1/charge", ChargeAPI)
|
app.GET("/v1/charge", ChargeAPI)
|
||||||
|
app.GET("/v1/plans", PlanAPI)
|
||||||
app.GET("/dashboard/billing/usage", GetBillingUsage)
|
app.GET("/dashboard/billing/usage", GetBillingUsage)
|
||||||
app.GET("/dashboard/billing/subscription", GetSubscription)
|
app.GET("/dashboard/billing/subscription", GetSubscription)
|
||||||
app.POST("/v1/chat/completions", ChatRelayAPI)
|
app.POST("/v1/chat/completions", ChatRelayAPI)
|
||||||
|
Loading…
Reference in New Issue
Block a user