Skip to content

Commit fe00b0c

Browse files
committed
Add baichuan
1 parent a6b1f90 commit fe00b0c

10 files changed

Lines changed: 180 additions & 1 deletion

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"lodash-es": "^4.17.21",
7474
"lucide-react": "^0.264.0",
7575
"md5": "^2.3.0",
76+
"nanoid": "^4.0.2",
7677
"ofetch": "^1.1.1",
7778
"plausible-tracker": "^0.3.8",
7879
"react": "^18.2.0",

src/app/bots/baichuan/api.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ofetch } from 'ofetch'
2+
import { customAlphabet } from 'nanoid'
3+
import { ChatError, ErrorCode } from '~utils/errors'
4+
5+
interface UserInfo {
6+
id: number
7+
}
8+
9+
export async function getUserInfo(): Promise<UserInfo> {
10+
const resp = await ofetch<{ data?: UserInfo; code: number; msg: string }>(
11+
'https://www.baichuan-ai.com/api/user/user-info',
12+
)
13+
if (resp.code === 401) {
14+
throw new ChatError('请先登录百川账号', ErrorCode.BAICHUAN_WEB_UNAUTHORIZED)
15+
}
16+
if (resp.code !== 200) {
17+
throw new Error(`Error: ${resp.code} ${resp.msg}`)
18+
}
19+
return resp.data!
20+
}
21+
22+
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789')
23+
24+
function randomString(length: number) {
25+
return nanoid(length)
26+
}
27+
28+
export function generateSessionId() {
29+
return 'p' + randomString(10)
30+
}
31+
32+
export function generateMessageId() {
33+
return 'U' + randomString(14)
34+
}

src/app/bots/baichuan/index.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { AbstractBot, SendMessageParams } from '../abstract-bot'
2+
import { requestHostPermission } from '~app/utils/permissions'
3+
import { ChatError, ErrorCode } from '~utils/errors'
4+
import { uuid } from '~utils'
5+
import { generateMessageId, generateSessionId, getUserInfo } from './api'
6+
import { streamAsyncIterable } from '~utils/stream-async-iterable'
7+
8+
interface Message {
9+
id: string
10+
createdAt: number
11+
data: string
12+
from: 0 | 1 // human | bot
13+
}
14+
15+
interface ConversationContext {
16+
conversationId: string
17+
historyMessages: Message[]
18+
userId: number
19+
lastMessageId?: string
20+
}
21+
22+
export class BaichuanWebBot extends AbstractBot {
23+
private conversationContext?: ConversationContext
24+
25+
async doSendMessage(params: SendMessageParams) {
26+
if (!(await requestHostPermission('https://*.baichuan-ai.com/'))) {
27+
throw new ChatError('Missing baichuan-ai.com permission', ErrorCode.MISSING_HOST_PERMISSION)
28+
}
29+
30+
if (!this.conversationContext) {
31+
const conversationId = generateSessionId()
32+
const userInfo = await getUserInfo()
33+
this.conversationContext = { conversationId, historyMessages: [], userId: userInfo.id }
34+
}
35+
36+
const { conversationId, lastMessageId, historyMessages, userId } = this.conversationContext
37+
38+
const message: Message = {
39+
id: generateMessageId(),
40+
createdAt: Date.now(),
41+
data: params.prompt,
42+
from: 0,
43+
}
44+
45+
const resp = await fetch('https://www.baichuan-ai.com/api/chat/v1/chat', {
46+
method: 'POST',
47+
signal: params.signal,
48+
headers: {
49+
'Content-Type': 'application/json',
50+
},
51+
body: JSON.stringify({
52+
stream: true,
53+
request_id: uuid(),
54+
app_info: { id: 10001, name: 'baichuan_web' },
55+
user_info: { id: userId, status: 1 },
56+
prompt: {
57+
id: message.id,
58+
data: message.data,
59+
from: message.from,
60+
parent_id: lastMessageId || 0,
61+
created_at: message.createdAt,
62+
},
63+
session_info: { id: conversationId, name: '新的对话', created_at: Date.now() },
64+
parameters: {
65+
repetition_penalty: -1,
66+
temperature: -1,
67+
top_k: -1,
68+
top_p: -1,
69+
max_new_tokens: -1,
70+
do_sample: -1,
71+
regenerate: 0,
72+
},
73+
history: historyMessages,
74+
}),
75+
})
76+
77+
const decoder = new TextDecoder()
78+
let result = ''
79+
let answerMessageId: string | undefined
80+
81+
for await (const uint8Array of streamAsyncIterable(resp.body!)) {
82+
const str = decoder.decode(uint8Array)
83+
console.debug('baichuan', str)
84+
const lines = str.split('\n')
85+
for (const line of lines) {
86+
if (!line) {
87+
continue
88+
}
89+
const data = JSON.parse(line)
90+
answerMessageId = data.answer.id
91+
const text = data.answer.data
92+
if (text) {
93+
result += text
94+
params.onEvent({ type: 'UPDATE_ANSWER', data: { text: result } })
95+
}
96+
}
97+
}
98+
99+
this.conversationContext.historyMessages.push(message)
100+
if (answerMessageId) {
101+
this.conversationContext.lastMessageId = answerMessageId
102+
if (result) {
103+
this.conversationContext.historyMessages.push({
104+
id: answerMessageId,
105+
data: result,
106+
createdAt: Date.now(),
107+
from: 1,
108+
})
109+
}
110+
}
111+
112+
params.onEvent({ type: 'DONE' })
113+
}
114+
115+
resetConversation() {
116+
this.conversationContext = undefined
117+
}
118+
119+
get name() {
120+
return '百川大模型'
121+
}
122+
}

src/app/bots/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { BaichuanWebBot } from './baichuan'
12
import { BardBot } from './bard'
23
import { BingWebBot } from './bing'
34
import { ChatGPTBot } from './chatgpt'
@@ -23,6 +24,7 @@ export type BotId =
2324
| 'guanaco'
2425
| 'wizardlm'
2526
| 'qianwen'
27+
| 'baichuan'
2628

2729
export function createBotInstance(botId: BotId) {
2830
switch (botId) {
@@ -56,6 +58,8 @@ export function createBotInstance(botId: BotId) {
5658
return new PiBot()
5759
case 'qianwen':
5860
return new QianwenWebBot()
61+
case 'baichuan':
62+
return new BaichuanWebBot()
5963
}
6064
}
6165

src/app/components/Chat/ErrorAction.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ const ErrorAction: FC<{ error: ChatError }> = ({ error }) => {
8181
</a>
8282
)
8383
}
84+
if (error.code === ErrorCode.BAICHUAN_WEB_UNAUTHORIZED) {
85+
return (
86+
<a href="https://www.baichuan-ai.com" target="_blank" rel="noreferrer">
87+
<ActionButton text={t('Login at baichuan-ai.com')} />
88+
</a>
89+
)
90+
}
8491
if (error.code === ErrorCode.GPT4_MODEL_WAITLIST) {
8592
return (
8693
<a href="https://openai.com/waitlist/gpt-4-api" target="_blank" rel="noreferrer">

src/app/components/Settings/EnabledBotsSettings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const EnabledBotsSettings: FC<Props> = ({ userConfig, updateConfigValue }) => {
2828
)
2929

3030
return (
31-
<div className="flex flex-row gap-3 flex-wrap max-w-[700px]">
31+
<div className="flex flex-row gap-3 flex-wrap max-w-[720px]">
3232
{Object.entries(CHATBOTS).map(([botId, bot]) => {
3333
const enabled = userConfig.enabledBots.includes(botId as BotId)
3434
return (

src/app/consts.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import vicunaLogo from '~/assets/vicuna-logo.jpg'
1313
import wizardlmLogo from '~/assets/wizardlm-logo.png'
1414
import xunfeiLogo from '~/assets/xunfei-logo.png'
1515
import qianwenLogo from '~/assets/qianwen-logo.png'
16+
import baichuanLogo from '~/assets/baichuan-logo.png'
1617
import { BotId } from './bots'
1718

1819
export const CHATBOTS: Record<BotId, { name: string; avatar: any }> = {
@@ -76,6 +77,10 @@ export const CHATBOTS: Record<BotId, { name: string; avatar: any }> = {
7677
name: 'Qianwen',
7778
avatar: qianwenLogo,
7879
},
80+
baichuan: {
81+
name: 'Baichuan',
82+
avatar: baichuanLogo,
83+
},
7984
}
8085

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

src/assets/baichuan-logo.png

1.17 KB
Loading

src/utils/errors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export enum ErrorCode {
2020
CLAUDE_WEB_UNAUTHORIZED = 'CLAUDE_WEB_UNAUTHORIZED',
2121
CLAUDE_WEB_UNAVAILABLE = 'CLAUDE_WEB_UNAVAILABLE',
2222
QIANWEN_WEB_UNAUTHORIZED = 'QIANWEN_WEB_UNAUTHORIZED',
23+
BAICHUAN_WEB_UNAUTHORIZED = 'BAICHUAN_WEB_UNAUTHORIZED',
2324
}
2425

2526
export class ChatError extends Error {

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3943,6 +3943,11 @@ nanoid@^3.3.6:
39433943
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
39443944
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
39453945

3946+
nanoid@^4.0.2:
3947+
version "4.0.2"
3948+
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e"
3949+
integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==
3950+
39463951
natural-compare-lite@^1.4.0:
39473952
version "1.4.0"
39483953
resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"

0 commit comments

Comments
 (0)