Skip to content

Commit bde2f0b

Browse files
committed
Add tongyi qianwen bot
1 parent 885a178 commit bde2f0b

9 files changed

Lines changed: 146 additions & 0 deletions

File tree

manifest.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ export default defineManifest(async (env) => {
6262
enabled: true,
6363
path: 'src/rules/ddg.json',
6464
},
65+
{
66+
id: 'ruleset_qianwen',
67+
enabled: true,
68+
path: 'src/rules/qianwen.json',
69+
},
6570
],
6671
},
6772
}

src/app/bots/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ChatGPTBot } from './chatgpt'
44
import { ClaudeBot } from './claude'
55
import { LMSYSBot } from './lmsys'
66
import { PiBot } from './pi'
7+
import { QianwenWebBot } from './tongyi'
78
import { XunfeiBot } from './xunfei'
89

910
export type BotId =
@@ -21,6 +22,7 @@ export type BotId =
2122
| 'pi'
2223
| 'guanaco'
2324
| 'wizardlm'
25+
| 'qianwen'
2426

2527
export function createBotInstance(botId: BotId) {
2628
switch (botId) {
@@ -52,6 +54,8 @@ export function createBotInstance(botId: BotId) {
5254
return new LMSYSBot('wizardlm-13b')
5355
case 'pi':
5456
return new PiBot()
57+
case 'qianwen':
58+
return new QianwenWebBot()
5559
}
5660
}
5761

src/app/bots/tongyi/api.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ofetch } from 'ofetch'
2+
import { ChatError, ErrorCode } from '~utils/errors'
3+
4+
interface CreationResponse {
5+
data: {
6+
sessionId: string
7+
}
8+
success: boolean
9+
errorMsg: string | null
10+
errorCode: string | null
11+
}
12+
13+
export async function createConversation(firstQuery: string) {
14+
const resp = await ofetch<CreationResponse>('https://qianwen.aliyun.com/addSession', {
15+
method: 'POST',
16+
body: { firstQuery },
17+
headers: {
18+
'X-Xsrf-Token': '7a3dae93-1d29-4eb6-9940-34efad5a2b78',
19+
},
20+
})
21+
if (!resp.success) {
22+
if (resp.errorCode === '4000') {
23+
throw new ChatError('请先登录通义千问账号', ErrorCode.QIANWEN_WEB_UNAUTHORIZED)
24+
}
25+
throw new Error(`Error: ${resp.errorCode} ${resp.errorMsg}`)
26+
}
27+
return resp.data.sessionId
28+
}

src/app/bots/tongyi/index.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { parseSSEResponse } from '~utils/sse'
2+
import { AbstractBot, SendMessageParams } from '../abstract-bot'
3+
import { requestHostPermission } from '~app/utils/permissions'
4+
import { ChatError, ErrorCode } from '~utils/errors'
5+
import { createConversation } from './api'
6+
import { uuid } from '~utils'
7+
8+
function generateMessageId() {
9+
return uuid().replace(/-/g, '')
10+
}
11+
12+
interface ConversationContext {
13+
conversationId: string
14+
lastMessageId?: string
15+
}
16+
17+
export class QianwenWebBot extends AbstractBot {
18+
private conversationContext?: ConversationContext
19+
20+
async doSendMessage(params: SendMessageParams) {
21+
if (!(await requestHostPermission('https://qianwen.aliyun.com/'))) {
22+
throw new ChatError('Missing qianwen.aliyun.com permission', ErrorCode.MISSING_HOST_PERMISSION)
23+
}
24+
25+
if (!this.conversationContext) {
26+
const conversationId = await createConversation(params.prompt)
27+
this.conversationContext = { conversationId }
28+
}
29+
30+
const resp = await fetch('https://qianwen.aliyun.com/conversation', {
31+
method: 'POST',
32+
signal: params.signal,
33+
headers: {
34+
'Content-Type': 'application/json',
35+
'X-Xsrf-Token': '7a3dae93-1d29-4eb6-9940-34efad5a2b78',
36+
},
37+
body: JSON.stringify({
38+
action: 'next',
39+
msgId: generateMessageId(),
40+
parentMsgId: this.conversationContext.lastMessageId || '0',
41+
contents: [{ contentType: 'text', content: params.prompt }],
42+
timeout: 17,
43+
sessionId: this.conversationContext.conversationId,
44+
model: '',
45+
userAction: 'chat',
46+
openSearch: true,
47+
}),
48+
})
49+
50+
await parseSSEResponse(resp, (message) => {
51+
console.debug('qianwen sse', message)
52+
const data = JSON.parse(message)
53+
const text = data.content[0]
54+
if (text) {
55+
params.onEvent({ type: 'UPDATE_ANSWER', data: { text } })
56+
}
57+
if (data.stopReason === 'stop') {
58+
this.conversationContext!.lastMessageId = data.msgId
59+
params.onEvent({ type: 'DONE' })
60+
}
61+
})
62+
}
63+
64+
resetConversation() {
65+
this.conversationContext = undefined
66+
}
67+
68+
get name() {
69+
return 'Tongyi Qianwen'
70+
}
71+
}

src/app/components/Chat/ErrorAction.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ const ErrorAction: FC<{ error: ChatError }> = ({ error }) => {
7474
</a>
7575
)
7676
}
77+
if (error.code === ErrorCode.QIANWEN_WEB_UNAUTHORIZED) {
78+
return (
79+
<a href="https://qianwen.aliyun.com" target="_blank" rel="noreferrer">
80+
<ActionButton text={t('Login at qianwen.aliyun.com')} />
81+
</a>
82+
)
83+
}
7784
if (error.code === ErrorCode.GPT4_MODEL_WAITLIST) {
7885
return (
7986
<a href="https://openai.com/waitlist/gpt-4-api" target="_blank" rel="noreferrer">

src/app/consts.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import rwkvLogo from '~/assets/rwkv-logo.png'
1212
import vicunaLogo from '~/assets/vicuna-logo.jpg'
1313
import wizardlmLogo from '~/assets/wizardlm-logo.png'
1414
import xunfeiLogo from '~/assets/xunfei-logo.png'
15+
import qianwenLogo from '~/assets/qianwen-logo.png'
1516
import { BotId } from './bots'
1617

1718
export const CHATBOTS: Record<BotId, { name: string; avatar: any }> = {
@@ -71,6 +72,10 @@ export const CHATBOTS: Record<BotId, { name: string; avatar: any }> = {
7172
name: 'WizardLM',
7273
avatar: wizardlmLogo,
7374
},
75+
qianwen: {
76+
name: 'Qianwen',
77+
avatar: qianwenLogo,
78+
},
7479
}
7580

7681
export const CHATGPT_HOME_URL = 'https://chat.openai.com'

src/assets/qianwen-logo.png

5.1 KB
Loading

src/rules/qianwen.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[
2+
{
3+
"id": 1,
4+
"priority": 1,
5+
"action": {
6+
"type": "modifyHeaders",
7+
"requestHeaders": [
8+
{
9+
"header": "origin",
10+
"operation": "set",
11+
"value": "https://qianwen.aliyun.com"
12+
},
13+
{
14+
"header": "referer",
15+
"operation": "set",
16+
"value": "https://qianwen.aliyun.com/chat"
17+
}
18+
]
19+
},
20+
"condition": {
21+
"requestDomains": ["qianwen.aliyun.com"],
22+
"resourceTypes": ["xmlhttprequest"]
23+
}
24+
}
25+
]

src/utils/errors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export enum ErrorCode {
1919
CHATGPT_INSUFFICIENT_QUOTA = 'CHATGPT_INSUFFICIENT_QUOTA',
2020
CLAUDE_WEB_UNAUTHORIZED = 'CLAUDE_WEB_UNAUTHORIZED',
2121
CLAUDE_WEB_UNAVAILABLE = 'CLAUDE_WEB_UNAVAILABLE',
22+
QIANWEN_WEB_UNAUTHORIZED = 'QIANWEN_WEB_UNAUTHORIZED',
2223
}
2324

2425
export class ChatError extends Error {

0 commit comments

Comments
 (0)