Skip to content

Commit ed84f93

Browse files
authored
Merge pull request #1177 from QuantumNous/alpha
merge alpha to main
2 parents ce2fba7 + 6bf8a72 commit ed84f93

File tree

13 files changed

+290
-86
lines changed

13 files changed

+290
-86
lines changed

relay/channel/ali/adaptor.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
3131
switch info.RelayMode {
3232
case constant.RelayModeEmbeddings:
3333
fullRequestURL = fmt.Sprintf("%s/api/v1/services/embeddings/text-embedding/text-embedding", info.BaseUrl)
34+
case constant.RelayModeRerank:
35+
fullRequestURL = fmt.Sprintf("%s/api/v1/services/rerank/text-rerank/text-rerank", info.BaseUrl)
3436
case constant.RelayModeImagesGenerations:
3537
fullRequestURL = fmt.Sprintf("%s/api/v1/services/aigc/text2image/image-synthesis", info.BaseUrl)
3638
case constant.RelayModeCompletions:
@@ -76,7 +78,7 @@ func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInf
7678
}
7779

7880
func (a *Adaptor) ConvertRerankRequest(c *gin.Context, relayMode int, request dto.RerankRequest) (any, error) {
79-
return nil, errors.New("not implemented")
81+
return ConvertRerankRequest(request), nil
8082
}
8183

8284
func (a *Adaptor) ConvertEmbeddingRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.EmbeddingRequest) (any, error) {
@@ -103,6 +105,8 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycom
103105
err, usage = aliImageHandler(c, resp, info)
104106
case constant.RelayModeEmbeddings:
105107
err, usage = aliEmbeddingHandler(c, resp)
108+
case constant.RelayModeRerank:
109+
err, usage = RerankHandler(c, resp, info)
106110
default:
107111
if info.IsStream {
108112
err, usage = openai.OaiStreamHandler(c, resp, info)

relay/channel/ali/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var ModelList = []string{
88
"qwq-32b",
99
"qwen3-235b-a22b",
1010
"text-embedding-v1",
11+
"gte-rerank-v2",
1112
}
1213

1314
var ChannelName = "ali"

relay/channel/ali/dto.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package ali
22

3+
import "one-api/dto"
4+
35
type AliMessage struct {
46
Content string `json:"content"`
57
Role string `json:"role"`
@@ -97,3 +99,28 @@ type AliImageRequest struct {
9799
} `json:"parameters,omitempty"`
98100
ResponseFormat string `json:"response_format,omitempty"`
99101
}
102+
103+
type AliRerankParameters struct {
104+
TopN *int `json:"top_n,omitempty"`
105+
ReturnDocuments *bool `json:"return_documents,omitempty"`
106+
}
107+
108+
type AliRerankInput struct {
109+
Query string `json:"query"`
110+
Documents []any `json:"documents"`
111+
}
112+
113+
type AliRerankRequest struct {
114+
Model string `json:"model"`
115+
Input AliRerankInput `json:"input"`
116+
Parameters AliRerankParameters `json:"parameters,omitempty"`
117+
}
118+
119+
type AliRerankResponse struct {
120+
Output struct {
121+
Results []dto.RerankResponseResult `json:"results"`
122+
} `json:"output"`
123+
Usage AliUsage `json:"usage"`
124+
RequestId string `json:"request_id"`
125+
AliError
126+
}

relay/channel/ali/rerank.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package ali
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"net/http"
7+
"one-api/dto"
8+
relaycommon "one-api/relay/common"
9+
"one-api/service"
10+
11+
"github.com/gin-gonic/gin"
12+
)
13+
14+
func ConvertRerankRequest(request dto.RerankRequest) *AliRerankRequest {
15+
returnDocuments := request.ReturnDocuments
16+
if returnDocuments == nil {
17+
t := true
18+
returnDocuments = &t
19+
}
20+
return &AliRerankRequest{
21+
Model: request.Model,
22+
Input: AliRerankInput{
23+
Query: request.Query,
24+
Documents: request.Documents,
25+
},
26+
Parameters: AliRerankParameters{
27+
TopN: &request.TopN,
28+
ReturnDocuments: returnDocuments,
29+
},
30+
}
31+
}
32+
33+
func RerankHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) {
34+
responseBody, err := io.ReadAll(resp.Body)
35+
if err != nil {
36+
return service.OpenAIErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil
37+
}
38+
err = resp.Body.Close()
39+
if err != nil {
40+
return service.OpenAIErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
41+
}
42+
43+
var aliResponse AliRerankResponse
44+
err = json.Unmarshal(responseBody, &aliResponse)
45+
if err != nil {
46+
return service.OpenAIErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil
47+
}
48+
49+
if aliResponse.Code != "" {
50+
return &dto.OpenAIErrorWithStatusCode{
51+
Error: dto.OpenAIError{
52+
Message: aliResponse.Message,
53+
Type: aliResponse.Code,
54+
Param: aliResponse.RequestId,
55+
Code: aliResponse.Code,
56+
},
57+
StatusCode: resp.StatusCode,
58+
}, nil
59+
}
60+
61+
usage := dto.Usage{
62+
PromptTokens: aliResponse.Usage.TotalTokens,
63+
CompletionTokens: 0,
64+
TotalTokens: aliResponse.Usage.TotalTokens,
65+
}
66+
rerankResponse := dto.RerankResponse{
67+
Results: aliResponse.Output.Results,
68+
Usage: usage,
69+
}
70+
71+
jsonResponse, err := json.Marshal(rerankResponse)
72+
if err != nil {
73+
return service.OpenAIErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), nil
74+
}
75+
c.Writer.Header().Set("Content-Type", "application/json")
76+
c.Writer.WriteHeader(resp.StatusCode)
77+
_, err = c.Writer.Write(jsonResponse)
78+
if err != nil {
79+
return service.OpenAIErrorWrapper(err, "write_response_body_failed", http.StatusInternalServerError), nil
80+
}
81+
82+
return nil, &usage
83+
}
Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import React, { useContext, useEffect, useState } from 'react';
2-
import { Spin, Typography, Space } from '@douyinfe/semi-ui';
32
import { useNavigate, useSearchParams } from 'react-router-dom';
3+
import { useTranslation } from 'react-i18next';
44
import { API, showError, showSuccess, updateAPI, setUserData } from '../../helpers';
55
import { UserContext } from '../../context/User';
6+
import Loading from '../common/Loading';
67

78
const OAuth2Callback = (props) => {
9+
const { t } = useTranslation();
810
const [searchParams, setSearchParams] = useSearchParams();
911

1012
const [userState, userDispatch] = useContext(UserContext);
11-
const [prompt, setPrompt] = useState('处理中...');
12-
const [processing, setProcessing] = useState(true);
13+
const [prompt, setPrompt] = useState(t('处理中...'));
1314

1415
let navigate = useNavigate();
1516

@@ -20,25 +21,25 @@ const OAuth2Callback = (props) => {
2021
const { success, message, data } = res.data;
2122
if (success) {
2223
if (message === 'bind') {
23-
showSuccess('绑定成功!');
24-
navigate('/setting');
24+
showSuccess(t('绑定成功!'));
25+
navigate('/console/setting');
2526
} else {
2627
userDispatch({ type: 'login', payload: data });
2728
localStorage.setItem('user', JSON.stringify(data));
2829
setUserData(data);
2930
updateAPI();
30-
showSuccess('登录成功!');
31-
navigate('/token');
31+
showSuccess(t('登录成功!'));
32+
navigate('/console/token');
3233
}
3334
} else {
3435
showError(message);
3536
if (count === 0) {
36-
setPrompt(`操作失败,重定向至登录界面中...`);
37-
navigate('/setting'); // in case this is failed to bind GitHub
37+
setPrompt(t('操作失败,重定向至登录界面中...'));
38+
navigate('/console/setting'); // in case this is failed to bind GitHub
3839
return;
3940
}
4041
count++;
41-
setPrompt(`出现错误,第 ${count} 次重试中...`);
42+
setPrompt(t('出现错误,第 ${count} 次重试中...', { count }));
4243
await new Promise((resolve) => setTimeout(resolve, count * 2000));
4344
await sendCode(code, state, count);
4445
}
@@ -50,17 +51,7 @@ const OAuth2Callback = (props) => {
5051
sendCode(code, state, 0).then();
5152
}, []);
5253

53-
return (
54-
<div className="flex items-center justify-center min-h-[300px] w-full bg-white rounded-lg shadow p-6">
55-
<Space vertical align="center">
56-
<Spin size="large" spinning={processing}>
57-
<div className="min-h-[200px] min-w-[200px] flex items-center justify-center">
58-
<Typography.Text type="secondary">{prompt}</Typography.Text>
59-
</div>
60-
</Spin>
61-
</Space>
62-
</div>
63-
);
54+
return <Loading prompt={prompt} />;
6455
};
6556

6657
export default OAuth2Callback;

web/src/components/auth/PasswordResetConfirm.js

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React, { useEffect, useState } from 'react';
22
import { API, copy, showError, showNotice, getLogo, getSystemName } from '../../helpers';
33
import { useSearchParams, Link } from 'react-router-dom';
4-
import { Button, Card, Form, Typography } from '@douyinfe/semi-ui';
5-
import { IconMail, IconLock } from '@douyinfe/semi-icons';
4+
import { Button, Card, Form, Typography, Banner } from '@douyinfe/semi-ui';
5+
import { IconMail, IconLock, IconCopy } from '@douyinfe/semi-icons';
66
import { useTranslation } from 'react-i18next';
77
import Background from '/example.png';
88

@@ -15,13 +15,14 @@ const PasswordResetConfirm = () => {
1515
token: '',
1616
});
1717
const { email, token } = inputs;
18+
const isValidResetLink = email && token;
1819

1920
const [loading, setLoading] = useState(false);
2021
const [disableButton, setDisableButton] = useState(false);
2122
const [countdown, setCountdown] = useState(30);
2223
const [newPassword, setNewPassword] = useState('');
23-
2424
const [searchParams, setSearchParams] = useSearchParams();
25+
const [formApi, setFormApi] = useState(null);
2526

2627
const logo = getLogo();
2728
const systemName = getSystemName();
@@ -30,10 +31,16 @@ const PasswordResetConfirm = () => {
3031
let token = searchParams.get('token');
3132
let email = searchParams.get('email');
3233
setInputs({
33-
token,
34-
email,
34+
token: token || '',
35+
email: email || '',
3536
});
36-
}, []);
37+
if (formApi) {
38+
formApi.setValues({
39+
email: email || '',
40+
newPassword: newPassword || ''
41+
});
42+
}
43+
}, [searchParams, newPassword, formApi]);
3744

3845
useEffect(() => {
3946
let countdownInterval = null;
@@ -49,7 +56,10 @@ const PasswordResetConfirm = () => {
4956
}, [disableButton, countdown]);
5057

5158
async function handleSubmit(e) {
52-
if (!email || !token) return;
59+
if (!email || !token) {
60+
showError(t('无效的重置链接,请重新发起密码重置请求'));
61+
return;
62+
}
5363
setDisableButton(true);
5464
setLoading(true);
5565
const res = await API.post(`/api/user/reset`, {
@@ -61,7 +71,7 @@ const PasswordResetConfirm = () => {
6171
let password = res.data.data;
6272
setNewPassword(password);
6373
await copy(password);
64-
showNotice(`${t('密码已重置并已复制到剪贴板')}: ${password}`);
74+
showNotice(`${t('密码已重置并已复制到剪贴板')} ${password}`);
6575
} else {
6676
showError(message);
6777
}
@@ -94,16 +104,28 @@ const PasswordResetConfirm = () => {
94104
<Title heading={3} className="text-gray-800 dark:text-gray-200">{t('密码重置确认')}</Title>
95105
</div>
96106
<div className="px-2 py-8">
97-
<Form className="space-y-3">
107+
{!isValidResetLink && (
108+
<Banner
109+
type="danger"
110+
description={t('无效的重置链接,请重新发起密码重置请求')}
111+
className="mb-4 !rounded-lg"
112+
closeIcon={null}
113+
/>
114+
)}
115+
<Form
116+
getFormApi={(api) => setFormApi(api)}
117+
initValues={{ email: email || '', newPassword: newPassword || '' }}
118+
className="space-y-4"
119+
>
98120
<Form.Input
99121
field="email"
100122
label={t('邮箱')}
101123
name="email"
102124
size="large"
103125
className="!rounded-md"
104-
value={email}
105-
readOnly
126+
disabled={true}
106127
prefix={<IconMail />}
128+
placeholder={email ? '' : t('等待获取邮箱信息...')}
107129
/>
108130

109131
{newPassword && (
@@ -113,14 +135,21 @@ const PasswordResetConfirm = () => {
113135
name="newPassword"
114136
size="large"
115137
className="!rounded-md"
116-
value={newPassword}
117-
readOnly
138+
disabled={true}
118139
prefix={<IconLock />}
119-
onClick={(e) => {
120-
e.target.select();
121-
navigator.clipboard.writeText(newPassword);
122-
showNotice(`${t('密码已复制到剪贴板')}: ${newPassword}`);
123-
}}
140+
suffix={
141+
<Button
142+
icon={<IconCopy />}
143+
type="tertiary"
144+
theme="borderless"
145+
onClick={async () => {
146+
await copy(newPassword);
147+
showNotice(`${t('密码已复制到剪贴板:')} ${newPassword}`);
148+
}}
149+
>
150+
{t('复制')}
151+
</Button>
152+
}
124153
/>
125154
)}
126155

@@ -133,9 +162,9 @@ const PasswordResetConfirm = () => {
133162
size="large"
134163
onClick={handleSubmit}
135164
loading={loading}
136-
disabled={disableButton || newPassword}
165+
disabled={disableButton || newPassword || !isValidResetLink}
137166
>
138-
{newPassword ? t('密码重置完成') : t('提交')}
167+
{newPassword ? t('密码重置完成') : t('确认重置密码')}
139168
</Button>
140169
</div>
141170
</Form>

web/src/components/auth/PasswordResetForm.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ const PasswordResetForm = () => {
5555
}
5656

5757
async function handleSubmit(e) {
58-
if (!email) return;
58+
if (!email) {
59+
showError(t('请输入邮箱地址'));
60+
return;
61+
}
5962
if (turnstileEnabled && turnstileToken === '') {
6063
showInfo(t('请稍后几秒重试,Turnstile 正在检查用户环境!'));
6164
return;

web/src/components/common/Loading.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const Loading = ({ prompt: name = '', size = 'large' }) => {
1414
tip={null}
1515
/>
1616
<span className="whitespace-nowrap mt-2 text-center" style={{ color: 'var(--semi-color-primary)' }}>
17-
{name ? t('加载{{name}}中...', { name }) : t('加载中...')}
17+
{name ? t('{{name}}', { name }) : t('加载中...')}
1818
</span>
1919
</div>
2020
</div>

0 commit comments

Comments
 (0)