From a5b84ba524982e64a6b736ee4d19818acf8fc220 Mon Sep 17 00:00:00 2001
From: HowieWu <98788152+utopeadia@users.noreply.github.com>
Date: Wed, 15 May 2024 10:25:01 +0800
Subject: [PATCH 01/12] Update adaptor.go
---
relay/channel/gemini/adaptor.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/relay/channel/gemini/adaptor.go b/relay/channel/gemini/adaptor.go
index daaadc5..d372d82 100644
--- a/relay/channel/gemini/adaptor.go
+++ b/relay/channel/gemini/adaptor.go
@@ -21,6 +21,7 @@ func (a *Adaptor) Init(info *relaycommon.RelayInfo, request dto.GeneralOpenAIReq
// 定义一个映射,存储模型名称和对应的版本
var modelVersionMap = map[string]string{
"gemini-1.5-pro-latest": "v1beta",
+ "gemini-1.5-flash-latest": "v1beta",
"gemini-ultra": "v1beta",
}
From 0f11461af3b7a660f0167c1b374b9de6e740c2c0 Mon Sep 17 00:00:00 2001
From: HowieWu <98788152+utopeadia@users.noreply.github.com>
Date: Wed, 15 May 2024 10:27:30 +0800
Subject: [PATCH 02/12] Update model-ratio.go
---
common/model-ratio.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/common/model-ratio.go b/common/model-ratio.go
index 7302731..8b66f49 100644
--- a/common/model-ratio.go
+++ b/common/model-ratio.go
@@ -79,6 +79,7 @@ var DefaultModelRatio = map[string]float64{
"gemini-1.0-pro-vision-001": 1,
"gemini-1.0-pro-001": 1,
"gemini-1.5-pro-latest": 1,
+ "gemini-1.5-flash-latest": 1,
"gemini-1.0-pro-latest": 1,
"gemini-1.0-pro-vision-latest": 1,
"gemini-ultra": 1,
From a3a6733fb5518e47257f15f5d25bd16657090fdc Mon Sep 17 00:00:00 2001
From: HowieWu <98788152+utopeadia@users.noreply.github.com>
Date: Wed, 15 May 2024 10:28:30 +0800
Subject: [PATCH 03/12] Update constant.go
---
relay/channel/gemini/constant.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/relay/channel/gemini/constant.go b/relay/channel/gemini/constant.go
index 5e833bc..621336b 100644
--- a/relay/channel/gemini/constant.go
+++ b/relay/channel/gemini/constant.go
@@ -5,7 +5,7 @@ const (
)
var ModelList = []string{
- "gemini-1.0-pro-latest", "gemini-1.0-pro-001", "gemini-1.5-pro-latest", "gemini-ultra",
+ "gemini-1.0-pro-latest", "gemini-1.0-pro-001", "gemini-1.5-pro-latest", "gemini-1.5-flash-latest", "gemini-ultra",
"gemini-1.0-pro-vision-latest", "gemini-1.0-pro-vision-001",
}
From 9dcec2772d778eb57c9fa60693183a15ec763a9c Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Wed, 15 May 2024 14:18:15 +0800
Subject: [PATCH 04/12] chore: update tiktoken (#254)
---
go.mod | 6 +++---
go.sum | 12 ++++++------
service/token_counter.go | 2 +-
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/go.mod b/go.mod
index a7dc491..81f487b 100644
--- a/go.mod
+++ b/go.mod
@@ -17,11 +17,11 @@ require (
github.com/go-playground/validator/v10 v10.19.0
github.com/go-redis/redis/v8 v8.11.5
github.com/golang-jwt/jwt v3.2.2+incompatible
- github.com/google/uuid v1.3.0
+ github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.0
github.com/jinzhu/copier v0.4.0
+ github.com/linux-do/tiktoken-go v0.7.0
github.com/pkg/errors v0.9.1
- github.com/pkoukk/tiktoken-go v0.1.6
github.com/samber/lo v1.39.0
github.com/shirou/gopsutil v3.21.11+incompatible
golang.org/x/crypto v0.21.0
@@ -42,7 +42,7 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
- github.com/dlclark/regexp2 v1.10.0 // indirect
+ github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
diff --git a/go.sum b/go.sum
index c6e80ba..49c71ef 100644
--- a/go.sum
+++ b/go.sum
@@ -32,8 +32,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
-github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
+github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
@@ -81,8 +81,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
@@ -124,6 +124,8 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/linux-do/tiktoken-go v0.7.0 h1:Kcm/miJ5gp77srtF8GQWnfq7W9kTaXEuHZg/g9IVEu8=
+github.com/linux-do/tiktoken-go v0.7.0/go.mod h1:9Vkdtp0ngi4USmrdSx984iuIQ5IMr0hnUdz4jZZTJb8=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -148,8 +150,6 @@ github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNc
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkoukk/tiktoken-go v0.1.6 h1:JF0TlJzhTbrI30wCvFuiw6FzP2+/bR+FIxUdgEAcUsw=
-github.com/pkoukk/tiktoken-go v0.1.6/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
diff --git a/service/token_counter.go b/service/token_counter.go
index 29da0f2..59138d8 100644
--- a/service/token_counter.go
+++ b/service/token_counter.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
- "github.com/pkoukk/tiktoken-go"
+ "github.com/linux-do/tiktoken-go"
"image"
"log"
"math"
From a3b3e6cc386045faacb425f99040be83f59c4aa3 Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Wed, 15 May 2024 16:32:00 +0800
Subject: [PATCH 05/12] chore: update InitTokenEncoders (#255)
---
service/token_counter.go | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/service/token_counter.go b/service/token_counter.go
index 59138d8..697ee15 100644
--- a/service/token_counter.go
+++ b/service/token_counter.go
@@ -26,6 +26,7 @@ func InitTokenEncoders() {
}
defaultTokenEncoder = gpt35TokenEncoder
gpt4TokenEncoder, err := tiktoken.EncodingForModel("gpt-4")
+ gpt4oTokenEncoder, err := tiktoken.EncodingForModel("gpt-4o")
if err != nil {
common.FatalLog(fmt.Sprintf("failed to get gpt-4 token encoder: %s", err.Error()))
}
@@ -33,7 +34,11 @@ func InitTokenEncoders() {
if strings.HasPrefix(model, "gpt-3.5") {
tokenEncoderMap[model] = gpt35TokenEncoder
} else if strings.HasPrefix(model, "gpt-4") {
- tokenEncoderMap[model] = gpt4TokenEncoder
+ if strings.HasPrefix(model, "gpt-4o") {
+ tokenEncoderMap[model] = gpt4oTokenEncoder
+ } else {
+ tokenEncoderMap[model] = gpt4TokenEncoder
+ }
} else {
tokenEncoderMap[model] = nil
}
From ff044de42a0c89b3c5675a4de22ee933d3b39752 Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Wed, 15 May 2024 20:17:27 +0800
Subject: [PATCH 06/12] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E6=A8=A1?=
=?UTF-8?q?=E5=9E=8B=E4=BB=B7=E6=A0=BC=E9=A1=B5=E9=9D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
model/pricing.go | 21 +++-----
relay/channel/openai/constant.go | 3 +-
web/src/components/ModelPricing.js | 78 ++++++++++++++++++++++--------
3 files changed, 65 insertions(+), 37 deletions(-)
diff --git a/model/pricing.go b/model/pricing.go
index c9685f3..237227c 100644
--- a/model/pricing.go
+++ b/model/pricing.go
@@ -35,27 +35,18 @@ func GetPricing(user *User, openAIModels []dto.OpenAIModels) []dto.ModelPricing
}
func updatePricing(openAIModels []dto.OpenAIModels) {
- modelRatios := common.GetModelRatios()
+ //modelRatios := common.GetModelRatios()
enabledModels := GetEnabledModels()
- allModels := make(map[string]string)
- for _, openAIModel := range openAIModels {
- if common.StringsContains(enabledModels, openAIModel.Id) {
- allModels[openAIModel.Id] = openAIModel.OwnedBy
- }
- }
- for model, _ := range modelRatios {
- if common.StringsContains(enabledModels, model) {
- if _, ok := allModels[model]; !ok {
- allModels[model] = "custom"
- }
- }
+ allModels := make(map[string]int)
+ for i, model := range enabledModels {
+ allModels[model] = i
}
+
pricingMap = make([]dto.ModelPricing, 0)
- for model, ownerBy := range allModels {
+ for model, _ := range allModels {
pricing := dto.ModelPricing{
Available: true,
ModelName: model,
- OwnerBy: ownerBy,
}
modelPrice, findPrice := common.GetModelPrice(model, false)
if findPrice {
diff --git a/relay/channel/openai/constant.go b/relay/channel/openai/constant.go
index 3a62b89..26ba147 100644
--- a/relay/channel/openai/constant.go
+++ b/relay/channel/openai/constant.go
@@ -7,7 +7,8 @@ var ModelList = []string{
"gpt-4", "gpt-4-0314", "gpt-4-0613", "gpt-4-1106-preview", "gpt-4-0125-preview",
"gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613",
"gpt-4-turbo-preview", "gpt-4-turbo", "gpt-4-turbo-2024-04-09",
- "gpt-4-vision-preview", "gpt-4o",
+ "gpt-4-vision-preview",
+ "gpt-4o", "gpt-4o-2024-05-13",
"text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large",
"text-curie-001", "text-babbage-001", "text-ada-001", "text-davinci-002", "text-davinci-003",
"text-moderation-latest", "text-moderation-stable",
diff --git a/web/src/components/ModelPricing.js b/web/src/components/ModelPricing.js
index 708d79e..c7a45d7 100644
--- a/web/src/components/ModelPricing.js
+++ b/web/src/components/ModelPricing.js
@@ -1,7 +1,16 @@
-import React, { useContext, useEffect, useState } from 'react';
+import React, { useContext, useEffect, useRef, useState } from 'react';
import { API, copy, showError, showSuccess } from '../helpers';
-import { Banner, Layout, Modal, Table, Tag, Tooltip } from '@douyinfe/semi-ui';
+import {
+ Banner,
+ Input,
+ Layout,
+ Modal,
+ Space,
+ Table,
+ Tag,
+ Tooltip,
+} from '@douyinfe/semi-ui';
import { stringToColor } from '../helpers/render.js';
import { UserContext } from '../context/User/index.js';
import Text from '@douyinfe/semi-ui/lib/es/typography/text';
@@ -45,6 +54,27 @@ function renderAvailable(available) {
}
const ModelPricing = () => {
+ const [filteredValue, setFilteredValue] = useState([]);
+ const compositionRef = useRef({ isComposition: false });
+
+ const handleChange = (value) => {
+ if (compositionRef.current.isComposition) {
+ return;
+ }
+ const newFilteredValue = value ? [value] : [];
+ setFilteredValue(newFilteredValue);
+ };
+ const handleCompositionStart = () => {
+ compositionRef.current.isComposition = true;
+ };
+
+ const handleCompositionEnd = (event) => {
+ compositionRef.current.isComposition = false;
+ const value = event.target.value;
+ const newFilteredValue = value ? [value] : [];
+ setFilteredValue(newFilteredValue);
+ };
+
const columns = [
{
title: '可用性',
@@ -52,28 +82,28 @@ const ModelPricing = () => {
render: (text, record, index) => {
return renderAvailable(text);
},
+ sorter: (a, b) => a.available - b.available,
},
{
- title: '提供者',
- dataIndex: 'owner_by',
- render: (text, record, index) => {
- return (
- <>
-
- {text}
-
- >
- );
- },
- },
- {
- title: '模型名称',
+ title: (
+
+ 模型名称
+
+
+ ),
dataIndex: 'model_name', // 以finish_time作为dataIndex
render: (text, record, index) => {
return (
<>
{
copyText(text);
@@ -84,6 +114,8 @@ const ModelPricing = () => {
>
);
},
+ onFilter: (value, record) => record.model_name.includes(value),
+ filteredValue,
},
{
title: '计费类型',
@@ -91,6 +123,7 @@ const ModelPricing = () => {
render: (text, record, index) => {
return renderQuotaType(parseInt(text));
},
+ sorter: (a, b) => a.quota_type - b.quota_type,
},
{
title: '模型倍率',
@@ -150,14 +183,17 @@ const ModelPricing = () => {
return a.quota_type - b.quota_type;
});
- // sort by owner_by, openai is max, other use localeCompare
+ // sort by model_name, start with gpt is max, other use localeCompare
models.sort((a, b) => {
- if (a.owner_by === 'openai') {
+ if (a.model_name.startsWith('gpt') && !b.model_name.startsWith('gpt')) {
return -1;
- } else if (b.owner_by === 'openai') {
+ } else if (
+ !a.model_name.startsWith('gpt') &&
+ b.model_name.startsWith('gpt')
+ ) {
return 1;
} else {
- return a.owner_by.localeCompare(b.owner_by);
+ return a.model_name.localeCompare(b.model_name);
}
});
From 93858c32d99a3e5f8a23dc678b04ab6cca1140be Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Wed, 15 May 2024 23:56:26 +0800
Subject: [PATCH 07/12] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E6=A8=A1?=
=?UTF-8?q?=E5=9E=8B=E4=BB=B7=E6=A0=BC=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
controller/model.go | 16 ++++++++--------
model/pricing.go | 10 +++++-----
router/api-router.go | 2 +-
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/controller/model.go b/controller/model.go
index de86ca3..5e1aa7d 100644
--- a/controller/model.go
+++ b/controller/model.go
@@ -108,8 +108,8 @@ func init() {
})
}
openAIModelsMap = make(map[string]dto.OpenAIModels)
- for _, model := range openAIModels {
- openAIModelsMap[model.Id] = model
+ for _, aiModel := range openAIModels {
+ openAIModelsMap[aiModel.Id] = aiModel
}
channelId2Models = make(map[int][]string)
for i := 1; i <= common.ChannelTypeDummy; i++ {
@@ -174,8 +174,8 @@ func DashboardListModels(c *gin.Context) {
func RetrieveModel(c *gin.Context) {
modelId := c.Param("model")
- if model, ok := openAIModelsMap[modelId]; ok {
- c.JSON(200, model)
+ if aiModel, ok := openAIModelsMap[modelId]; ok {
+ c.JSON(200, aiModel)
} else {
openAIError := dto.OpenAIError{
Message: fmt.Sprintf("The model '%s' does not exist", modelId),
@@ -191,12 +191,12 @@ func RetrieveModel(c *gin.Context) {
func GetPricing(c *gin.Context) {
userId := c.GetInt("id")
- user, _ := model.GetUserById(userId, true)
+ group, err := model.CacheGetUserGroup(userId)
groupRatio := common.GetGroupRatio("default")
- if user != nil {
- groupRatio = common.GetGroupRatio(user.Group)
+ if err != nil {
+ groupRatio = common.GetGroupRatio(group)
}
- pricing := model.GetPricing(user, openAIModels)
+ pricing := model.GetPricing(group)
c.JSON(200, gin.H{
"success": true,
"data": pricing,
diff --git a/model/pricing.go b/model/pricing.go
index 237227c..90d8bc7 100644
--- a/model/pricing.go
+++ b/model/pricing.go
@@ -13,16 +13,16 @@ var (
updatePricingLock sync.Mutex
)
-func GetPricing(user *User, openAIModels []dto.OpenAIModels) []dto.ModelPricing {
+func GetPricing(group string) []dto.ModelPricing {
updatePricingLock.Lock()
defer updatePricingLock.Unlock()
if time.Since(lastGetPricingTime) > time.Minute*1 || len(pricingMap) == 0 {
- updatePricing(openAIModels)
+ updatePricing()
}
- if user != nil {
+ if group != "" {
userPricingMap := make([]dto.ModelPricing, 0)
- models := GetGroupModels(user.Group)
+ models := GetGroupModels(group)
for _, pricing := range pricingMap {
if !common.StringsContains(models, pricing.ModelName) {
pricing.Available = false
@@ -34,7 +34,7 @@ func GetPricing(user *User, openAIModels []dto.OpenAIModels) []dto.ModelPricing
return pricingMap
}
-func updatePricing(openAIModels []dto.OpenAIModels) {
+func updatePricing() {
//modelRatios := common.GetModelRatios()
enabledModels := GetEnabledModels()
allModels := make(map[string]int)
diff --git a/router/api-router.go b/router/api-router.go
index add5c5f..b98a94e 100644
--- a/router/api-router.go
+++ b/router/api-router.go
@@ -20,7 +20,7 @@ func SetApiRouter(router *gin.Engine) {
apiRouter.GET("/about", controller.GetAbout)
//apiRouter.GET("/midjourney", controller.GetMidjourney)
apiRouter.GET("/home_page_content", controller.GetHomePageContent)
- apiRouter.GET("/pricing", middleware.CriticalRateLimit(), middleware.TryUserAuth(), controller.GetPricing)
+ apiRouter.GET("/pricing", middleware.TryUserAuth(), controller.GetPricing)
apiRouter.GET("/verification", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendEmailVerification)
apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendPasswordResetEmail)
apiRouter.POST("/user/reset", middleware.CriticalRateLimit(), controller.ResetPassword)
From 95ac7c343b701f4752719586b4345987e8a97b69 Mon Sep 17 00:00:00 2001
From: Boo p3psi
Date: Wed, 15 May 2024 23:20:06 +0800
Subject: [PATCH 08/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B8=A0=E9=81=93?=
=?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=B2=A1=E6=9C=89=E8=B5=B0=E6=A8=A1=E5=9E=8B?=
=?UTF-8?q?=E6=98=A0=E5=B0=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
controller/channel-test.go | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/controller/channel-test.go b/controller/channel-test.go
index 7474cb4..db03e75 100644
--- a/controller/channel-test.go
+++ b/controller/channel-test.go
@@ -64,7 +64,21 @@ func testChannel(channel *model.Channel, testModel string) (err error, openaiErr
} else {
testModel = adaptor.GetModelList()[0]
}
+ } else {
+ modelMapping := *channel.ModelMapping
+ if modelMapping != "" && modelMapping != "{}" {
+ modelMap := make(map[string]string)
+ err := json.Unmarshal([]byte(modelMapping), &modelMap)
+ if err != nil {
+ openaiErr := service.OpenAIErrorWrapperLocal(err, "unmarshal_model_mapping_failed", http.StatusInternalServerError).Error
+ return err, &openaiErr
+ }
+ if modelMap[testModel] != "" {
+ testModel = modelMap[testModel]
+ }
+ }
}
+
request := buildTestRequest()
request.Model = testModel
meta.UpstreamModelName = testModel
From d33b802dac7e4732bd705077485981a1724f056f Mon Sep 17 00:00:00 2001
From: Akarin
Date: Thu, 16 May 2024 14:05:44 +0800
Subject: [PATCH 09/12] Squashed commit of the following:
commit 5a6a0df45dee3dfbf2f65591a79fe5f2b74a49e6
Author: Akarin
Date: Thu May 16 14:05:28 2024 +0800
Revert "Update docker-image-amd64.yml"
This reverts commit 581343a78783bbd779e65b476e125af0e2b64ce5.
commit a0aec1bd030da2c6b25d9541199d598f16813a60
Merge: 5b46c7d 58abb38
Author: Jiayun Shen
Date: Thu May 16 06:46:51 2024 +0800
Merge branch 'main' of https://github.com/jimmyshjj/new-api
commit 58abb3864a89294d82f812cda9fe49ccf7e2dd91
Merge: 7d2c026 93858c3
Author: Akarin
Date: Thu May 16 06:46:00 2024 +0800
Merge branch 'Calcium-Ion:main' into main
commit 5b46c7dd8e6132d2be3b59c7b2ed6a4b84b93cef
Author: Jiayun Shen
Date: Thu May 16 06:45:00 2024 +0800
Update constants.go
Remove replaced Baidu models
commit 7d2c02679cd90b8b53f4145f83969b980a8c2095
Author: Akarin
Date: Wed May 15 23:40:50 2024 +0800
Update adaptor.go - Normalize model name to lowercase
Baidu's official model names may include mixed case letters, but their model APIs are case-sensitive and accept only lowercase. To ensure compatibility, the default behavior has been updated to convert model names to lowercase before constructing API requests.
commit 6bc168a39d9a6194d66f2f32b175e56de9295b2e
Merge: bb9fecd 910e76a
Author: Jiayun Shen
Date: Wed May 15 21:51:52 2024 +0800
Merge branch 'main' of https://github.com/jimmyshjj/new-api
commit 910e76ac94d7f5dca6254abb4d0669cbb762e724
Merge: 581343a ff044de
Author: Akarin
Date: Wed May 15 21:51:13 2024 +0800
Merge branch 'Calcium-Ion:main' into main
commit bb9fecd5bf2bd9f1859a4017e7e68f80bdb6685a
Author: Jiayun Shen
Date: Wed May 15 21:50:08 2024 +0800
update Baidu and 360 models
Add Baidu and 360 new models. Add Baidu completion ratio
commit 581343a78783bbd779e65b476e125af0e2b64ce5
Author: Akarin
Date: Wed May 15 19:41:34 2024 +0800
Update docker-image-amd64.yml
commit de17e2d95eec80f1eeae66e82dec4e9601cdee43
Merge: 046f653 a3b3e6c
Author: Akarin
Date: Wed May 15 19:22:09 2024 +0800
Merge branch 'Calcium-Ion:main' into main
commit 046f6537913ae8ad8ecf21019b64c0379331b3fd
Merge: 4164d51 7b58305
Author: Akarin
Date: Wed May 15 15:32:38 2024 +0800
Merge branch 'Calcium-Ion:main' into main
commit 4164d51207808283a18ca2728241fd5cddc4855f
Merge: ef35b07 c222bc8
Author: Akarin
Date: Wed May 15 11:19:13 2024 +0800
Merge branch 'Calcium-Ion:main' into main
commit ef35b072824b5095ecd2d1ed7ca9fa11673da2c4
Author: Jiayun Shen
Date: Tue May 14 19:17:32 2024 +0800
Update adaptor.go
Update frequently used model names from Baidu official docs and support custom models
---
common/model-ratio.go | 26 +++++++++++++++++++++++---
relay/channel/ai360/constants.go | 3 +++
relay/channel/baidu/adaptor.go | 17 +++++++++++++++++
relay/channel/baidu/constants.go | 18 +++++++++++++-----
4 files changed, 56 insertions(+), 8 deletions(-)
diff --git a/common/model-ratio.go b/common/model-ratio.go
index 8b66f49..b84a0a5 100644
--- a/common/model-ratio.go
+++ b/common/model-ratio.go
@@ -69,9 +69,17 @@ var DefaultModelRatio = map[string]float64{
"claude-3-haiku-20240307": 0.125, // $0.25 / 1M tokens
"claude-3-sonnet-20240229": 1.5, // $3 / 1M tokens
"claude-3-opus-20240229": 7.5, // $15 / 1M tokens
- "ERNIE-Bot": 0.8572, // ¥0.012 / 1k tokens
- "ERNIE-Bot-turbo": 0.5715, // ¥0.008 / 1k tokens
- "ERNIE-Bot-4": 8.572, // ¥0.12 / 1k tokens
+ "ERNIE-Bot": 0.8572, // ¥0.012 / 1k tokens //renamed to ERNIE-3.5-8K
+ "ERNIE-Bot-turbo": 0.5715, // ¥0.008 / 1k tokens //renamed to ERNIE-Lite-8K
+ "ERNIE-Bot-4": 8.572, // ¥0.12 / 1k tokens //renamed to ERNIE-4.0-8K
+ "ERNIE-4.0-8K": 8.572, // ¥0.12 / 1k tokens
+ "ERNIE-3.5-8K": 0.8572, // ¥0.012 / 1k tokens
+ "ERNIE-Speed-8K": 0.2858, // ¥0.004 / 1k tokens
+ "ERNIE-Speed-128K": 0.2858, // ¥0.004 / 1k tokens
+ "ERNIE-Lite-8K": 0.2143, // ¥0.003 / 1k tokens
+ "ERNIE-Tiny-8K": 0.0715, // ¥0.001 / 1k tokens
+ "ERNIE-Character-8K": 0.2858, // ¥0.004 / 1k tokens
+ "ERNIE-Functions-8K": 0.2858, // ¥0.004 / 1k tokens
"Embedding-V1": 0.1429, // ¥0.002 / 1k tokens
"PaLM-2": 1,
"gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
@@ -98,6 +106,9 @@ var DefaultModelRatio = map[string]float64{
"SparkDesk-v3.1": 1.2858, // ¥0.018 / 1k tokens
"SparkDesk-v3.5": 1.2858, // ¥0.018 / 1k tokens
"360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens
+ "360gpt-turbo": 0.0858, // ¥0.0012 / 1k tokens
+ "360gpt-turbo-responsibility-8k": 0.8572, // ¥0.012 / 1k tokens
+ "360gpt-pro": 0.8572, // ¥0.012 / 1k tokens
"embedding-bert-512-v1": 0.0715, // ¥0.001 / 1k tokens
"embedding_s1_v1": 0.0715, // ¥0.001 / 1k tokens
"semantic_similarity_s1_v1": 0.0715, // ¥0.001 / 1k tokens
@@ -289,6 +300,15 @@ func GetCompletionRatio(name string) float64 {
if strings.HasPrefix(name, "deepseek") {
return 2
}
+ if strings.HasPrefix(name, "ERNIE-Speed-") {
+ return 2
+ } else if strings.HasPrefix(name, "ERNIE-Lite-") {
+ return 2
+ } else if strings.HasPrefix(name, "ERNIE-Character") {
+ return 2
+ } else if strings.HasPrefix(name, "ERNIE-Functions") {
+ return 2
+ }
switch name {
case "llama2-70b-4096":
return 0.8 / 0.64
diff --git a/relay/channel/ai360/constants.go b/relay/channel/ai360/constants.go
index 82698fa..824231d 100644
--- a/relay/channel/ai360/constants.go
+++ b/relay/channel/ai360/constants.go
@@ -1,6 +1,9 @@
package ai360
var ModelList = []string{
+ "360gpt-turbo",
+ "360gpt-turbo-responsibility-8k",
+ "360gpt-pro",
"360GPT_S2_V9",
"embedding-bert-512-v1",
"embedding_s1_v1",
diff --git a/relay/channel/baidu/adaptor.go b/relay/channel/baidu/adaptor.go
index d2571dc..44c5e3f 100644
--- a/relay/channel/baidu/adaptor.go
+++ b/relay/channel/baidu/adaptor.go
@@ -9,6 +9,7 @@ import (
"one-api/relay/channel"
relaycommon "one-api/relay/common"
"one-api/relay/constant"
+ "strings"
)
type Adaptor struct {
@@ -33,8 +34,24 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant"
case "BLOOMZ-7B":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/bloomz_7b1"
+ case "ERNIE-4.0-8K":
+ fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro"
+ case "ERNIE-3.5-8K":
+ fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions"
+ case "ERNIE-Speed-8K":
+ fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie_speed"
+ case "ERNIE-Character-8K":
+ fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-char-8k"
+ case "ERNIE-Functions-8K":
+ fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-func-8k"
+ case "ERNIE-Lite-8K-0922":
+ fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant"
+ case "Yi-34B-Chat":
+ fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/yi_34b_chat"
case "Embedding-V1":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/embedding-v1"
+ default:
+ fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/" + strings.ToLower(info.UpstreamModelName)
}
var accessToken string
var err error
diff --git a/relay/channel/baidu/constants.go b/relay/channel/baidu/constants.go
index a0162bb..3cb96fc 100644
--- a/relay/channel/baidu/constants.go
+++ b/relay/channel/baidu/constants.go
@@ -1,11 +1,19 @@
package baidu
var ModelList = []string{
- "ERNIE-Bot-4",
- "ERNIE-Bot-8K",
- "ERNIE-Bot",
- "ERNIE-Speed",
- "ERNIE-Bot-turbo",
+ "ERNIE-3.5-8K",
+ "ERNIE-4.0-8K",
+ "ERNIE-Speed-8K",
+ "ERNIE-Speed-128K",
+ "ERNIE-Lite-8K",
+ "ERNIE-Tiny-8K",
+ "ERNIE-Character-8K",
+ "ERNIE-Functions-8K",
+ //"ERNIE-Bot-4",
+ //"ERNIE-Bot-8K",
+ //"ERNIE-Bot",
+ //"ERNIE-Speed",
+ //"ERNIE-Bot-turbo",
"Embedding-V1",
}
From 7003a4ed9481b2c21fda3faa2bf34d165907418d Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Thu, 16 May 2024 16:10:25 +0800
Subject: [PATCH 10/12] fix: try to fix sqlite database migration (#231)
---
model/main.go | 12 ++++++------
model/token.go | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/model/main.go b/model/main.go
index 7b1cd3d..b6ad2cb 100644
--- a/model/main.go
+++ b/model/main.go
@@ -93,12 +93,12 @@ func InitDB() (err error) {
if !common.IsMasterNode {
return nil
}
- if common.UsingMySQL {
- _, _ = sqlDB.Exec("DROP INDEX idx_channels_key ON channels;") // TODO: delete this line when most users have upgraded
- _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY action VARCHAR(40);") // TODO: delete this line when most users have upgraded
- _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY progress VARCHAR(30);") // TODO: delete this line when most users have upgraded
- _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY status VARCHAR(20);") // TODO: delete this line when most users have upgraded
- }
+ //if common.UsingMySQL {
+ // _, _ = sqlDB.Exec("DROP INDEX idx_channels_key ON channels;") // TODO: delete this line when most users have upgraded
+ // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY action VARCHAR(40);") // TODO: delete this line when most users have upgraded
+ // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY progress VARCHAR(30);") // TODO: delete this line when most users have upgraded
+ // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY status VARCHAR(20);") // TODO: delete this line when most users have upgraded
+ //}
common.SysLog("database migration started")
err = db.AutoMigrate(&Channel{})
if err != nil {
diff --git a/model/token.go b/model/token.go
index 1bbf6c4..056156e 100644
--- a/model/token.go
+++ b/model/token.go
@@ -11,7 +11,7 @@ import (
type Token struct {
Id int `json:"id"`
- UserId int `json:"user_id"`
+ UserId int `json:"user_id" gorm:"index"`
Key string `json:"key" gorm:"type:char(48);uniqueIndex"`
Status int `json:"status" gorm:"default:1"`
Name string `json:"name" gorm:"index" `
From 71dcf43c71fd336a3db9749374e2f85eb0200524 Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Thu, 16 May 2024 16:41:08 +0800
Subject: [PATCH 11/12] =?UTF-8?q?feat:=20=E6=97=A5=E5=BF=97=E6=98=BE?=
=?UTF-8?q?=E7=A4=BA=E9=87=8D=E8=AF=95=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
common/utils.go | 9 +++++++++
controller/relay.go | 7 +++++--
model/log.go | 9 +++++++++
relay/relay-text.go | 3 +++
web/src/components/LogsTable.js | 24 ++++++++++++++++++++++++
5 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/common/utils.go b/common/utils.go
index 3130020..6c89d41 100644
--- a/common/utils.go
+++ b/common/utils.go
@@ -258,3 +258,12 @@ func MapToJsonStrFloat(m map[string]float64) string {
}
return string(bytes)
}
+
+func StrToMap(str string) map[string]interface{} {
+ m := make(map[string]interface{})
+ err := json.Unmarshal([]byte(str), &m)
+ if err != nil {
+ return nil
+ }
+ return m
+}
diff --git a/controller/relay.go b/controller/relay.go
index 0bbd409..a066e5d 100644
--- a/controller/relay.go
+++ b/controller/relay.go
@@ -43,7 +43,7 @@ func Relay(c *gin.Context) {
group := c.GetString("group")
originalModel := c.GetString("original_model")
openaiErr := relayHandler(c, relayMode)
- useChannel := []int{channelId}
+ c.Set("use_channel", []string{fmt.Sprintf("%d", channelId)})
if openaiErr != nil {
go processChannelError(c, channelId, openaiErr)
} else {
@@ -56,7 +56,9 @@ func Relay(c *gin.Context) {
break
}
channelId = channel.Id
- useChannel = append(useChannel, channelId)
+ useChannel := c.GetStringSlice("use_channel")
+ useChannel = append(useChannel, fmt.Sprintf("%d", channelId))
+ c.Set("use_channel", useChannel)
common.LogInfo(c.Request.Context(), fmt.Sprintf("using channel #%d to retry (remain times %d)", channel.Id, i))
middleware.SetupContextForSelectedChannel(c, channel, originalModel)
@@ -67,6 +69,7 @@ func Relay(c *gin.Context) {
go processChannelError(c, channelId, openaiErr)
}
}
+ useChannel := c.GetStringSlice("use_channel")
if len(useChannel) > 1 {
retryLogStr := fmt.Sprintf("重试:%s", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(useChannel)), "->"), "[]"))
common.LogInfo(c.Request.Context(), retryLogStr)
diff --git a/model/log.go b/model/log.go
index 57c64d0..2e72b71 100644
--- a/model/log.go
+++ b/model/log.go
@@ -142,6 +142,15 @@ func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int
tx = tx.Where("created_at <= ?", endTimestamp)
}
err = tx.Order("id desc").Limit(num).Offset(startIdx).Omit("id").Find(&logs).Error
+ for i := range logs {
+ var otherMap map[string]interface{}
+ otherMap = common.StrToMap(logs[i].Other)
+ if otherMap != nil {
+ // delete admin
+ delete(otherMap, "admin_info")
+ }
+ logs[i].Other = common.MapToJsonStr(otherMap)
+ }
return logs, err
}
diff --git a/relay/relay-text.go b/relay/relay-text.go
index d5ee728..007b3b1 100644
--- a/relay/relay-text.go
+++ b/relay/relay-text.go
@@ -320,6 +320,9 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, textRe
other["group_ratio"] = groupRatio
other["completion_ratio"] = completionRatio
other["model_price"] = modelPrice
+ adminInfo := make(map[string]interface{})
+ adminInfo["use_channel"] = ctx.GetStringSlice("use_channel")
+ other["admin_info"] = adminInfo
model.RecordConsumeLog(ctx, relayInfo.UserId, relayInfo.ChannelId, promptTokens, completionTokens, logModel, tokenName, quota, logContent, relayInfo.TokenId, userQuota, int(useTimeSeconds), relayInfo.IsStream, other)
//if quota != 0 {
diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js
index 8efb9db..f8436ad 100644
--- a/web/src/components/LogsTable.js
+++ b/web/src/components/LogsTable.js
@@ -294,6 +294,30 @@ const LogsTable = () => {
);
},
},
+ {
+ title: '重试',
+ dataIndex: 'retry',
+ className: isAdmin() ? 'tableShow' : 'tableHiddle',
+ render: (text, record, index) => {
+ let content = '渠道:' + record.channel;
+ if (record.other !== '') {
+ let other = JSON.parse(record.other);
+ if (other.admin_info !== undefined) {
+ if (
+ other.admin_info.use_channel !== null &&
+ other.admin_info.use_channel !== undefined &&
+ other.admin_info.use_channel !== ''
+ ) {
+ // channel id array
+ let useChannel = other.admin_info.use_channel;
+ let useChannelStr = useChannel.join('->');
+ content = `渠道:${useChannelStr}`;
+ }
+ }
+ }
+ return isAdminUser ? {content}
: <>>;
+ },
+ },
{
title: '详情',
dataIndex: 'content',
From 5e07ff85eb13539e58f0ead9886b5ace80e17529 Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Thu, 16 May 2024 18:31:03 +0800
Subject: [PATCH 12/12] feat: pre to delete custom channel type
---
relay/constant/api_type.go | 4 ++--
relay/relay_adaptor.go | 2 +-
web/src/components/ChannelsTable.js | 7 +++++++
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/relay/constant/api_type.go b/relay/constant/api_type.go
index 8a1dbd6..943c407 100644
--- a/relay/constant/api_type.go
+++ b/relay/constant/api_type.go
@@ -15,7 +15,7 @@ const (
APITypeAIProxyLibrary
APITypeTencent
APITypeGemini
- APITypeZhipu_v4
+ APITypeZhipuV4
APITypeOllama
APITypePerplexity
APITypeAws
@@ -48,7 +48,7 @@ func ChannelType2APIType(channelType int) (int, bool) {
case common.ChannelTypeGemini:
apiType = APITypeGemini
case common.ChannelTypeZhipu_v4:
- apiType = APITypeZhipu_v4
+ apiType = APITypeZhipuV4
case common.ChannelTypeOllama:
apiType = APITypeOllama
case common.ChannelTypePerplexity:
diff --git a/relay/relay_adaptor.go b/relay/relay_adaptor.go
index 01e9cec..cf63054 100644
--- a/relay/relay_adaptor.go
+++ b/relay/relay_adaptor.go
@@ -41,7 +41,7 @@ func GetAdaptor(apiType int) channel.Adaptor {
return &xunfei.Adaptor{}
case constant.APITypeZhipu:
return &zhipu.Adaptor{}
- case constant.APITypeZhipu_v4:
+ case constant.APITypeZhipuV4:
return &zhipu_4v.Adaptor{}
case constant.APITypeOllama:
return &ollama.Adaptor{}
diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js
index 452309c..ad53999 100644
--- a/web/src/components/ChannelsTable.js
+++ b/web/src/components/ChannelsTable.js
@@ -6,6 +6,7 @@ import {
showError,
showInfo,
showSuccess,
+ showWarning,
timestamp2string,
} from '../helpers';
@@ -309,6 +310,12 @@ const ChannelsTable = () => {
const setChannelFormat = (channels) => {
for (let i = 0; i < channels.length; i++) {
+ if (channels[i].type === 8) {
+ showWarning(
+ '检测到您使用了“自定义渠道”类型,请更换为“OpenAI”渠道类型!',
+ );
+ showWarning('下个版本将不再支持“自定义渠道”类型!');
+ }
channels[i].key = '' + channels[i].id;
let test_models = [];
channels[i].models.split(',').forEach((item, index) => {