-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
302 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { ErnieBot } from '../../src/ErnieBot' | ||
|
||
const bot = new ErnieBot({ | ||
apiKey: process.env.BAIDU_BCE_API_KEY as string, | ||
secretKey: process.env.BAIDU_BCE_SK as string, | ||
}) | ||
|
||
;(async () => { | ||
await bot.sendStreamMessage({ | ||
initialMessages: [ | ||
{ | ||
role: 'user', | ||
content: '介绍下你自己', | ||
}, | ||
{ | ||
role: 'assistant', | ||
content: '我是小胖AI,你的个人助手', | ||
}, | ||
{ | ||
role: 'user', | ||
content: '你叫什么?', | ||
}, | ||
], | ||
onProgress(t) { | ||
process.stdout.write(t) | ||
}, | ||
onEnd(t) { | ||
console.log('end', t) | ||
}, | ||
}) | ||
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import axios from 'axios' | ||
|
||
async function getAccessToken(): Promise<string> { | ||
const url = | ||
`https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${process.env.BAIDU_BCE_API_KEY}&client_secret=${process.env.BAIDU_BCE_SK}` | ||
const response = await axios.post(url, '', { | ||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, | ||
}) | ||
return response.data.access_token | ||
} | ||
|
||
async function main() { | ||
const url = | ||
'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=' + | ||
(await getAccessToken()) | ||
const payload = { | ||
messages: [ | ||
{ | ||
role: 'user', | ||
content: '你是一个作家', | ||
}, | ||
{ | ||
role: 'assistant', | ||
content: '你好呀', | ||
}, | ||
{ | ||
role: 'user', | ||
content: '介绍下你自己', | ||
}, | ||
{ | ||
role: 'assistant', | ||
content: '我是文心一言', | ||
}, | ||
], | ||
} | ||
const response = await axios.post(url, payload, { | ||
headers: { 'Content-Type': 'application/json' }, | ||
}) | ||
console.log(response.data) | ||
} | ||
|
||
main() | ||
|
||
// { | ||
// id: 'as-cmmvbiyuf6', | ||
// object: 'chat.completion', | ||
// created: 1690189247, | ||
// result: '您好,我是文心一言,英文名是ERNIE Bot。我能够与人对话互动,回答问题,协助创作,高效便捷地帮助人们获取信息、知识和灵感。', | ||
// is_truncated: false, | ||
// need_clear_history: false, | ||
// usage: { prompt_tokens: 7, completion_tokens: 49, total_tokens: 56 } | ||
// } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import axios from 'axios' | ||
import type { | ||
IErnieBotAccessToken, | ||
IErnieBotAccessTokenErr, | ||
IErnieBotSendMessageOpts, | ||
IErnieBotUserMessage, | ||
IErnieBotAssistantMessage, | ||
TErnieBotCommonMessage, | ||
IErnieBotResponseErr, | ||
} from './ErnieBotTypes' | ||
|
||
interface IErnieBotParams { | ||
apiKey: string | ||
secretKey: string | ||
debug?: boolean | ||
} | ||
|
||
export class ErnieBot { | ||
#apiKey = '' | ||
#model = '' | ||
#secretKey = '' | ||
#debug = false | ||
constructor(opts: IErnieBotParams) { | ||
const { apiKey, secretKey, debug = false } = opts | ||
this.#apiKey = apiKey | ||
this.#secretKey = secretKey | ||
this.#debug = debug | ||
} | ||
private async getAccessToken(): Promise<string> { | ||
const url = `https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${ | ||
this.#apiKey | ||
}&client_secret=${this.#secretKey}` | ||
|
||
const response = await axios(url, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Accept: 'application/json', | ||
}, | ||
validateStatus: (status) => true, | ||
}) | ||
|
||
const data = response.data as IErnieBotAccessToken | ||
if (!data.access_token) | ||
throw { msg: 'access_token 获取失败', status: response.status } | ||
return data.access_token | ||
} | ||
async sendStreamMessage(opts: IErnieBotSendMessageOpts) { | ||
try { | ||
const accessToken = await this.getAccessToken() | ||
const apiUrl = `https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=${accessToken}` | ||
|
||
const requestData = { | ||
messages: opts.initialMessages, | ||
stream: true, | ||
} | ||
|
||
const headers = { | ||
'Content-Type': 'application/json', | ||
} | ||
|
||
const axiosResponse = await axios.post(apiUrl, requestData, { | ||
headers, | ||
responseType: 'stream', | ||
}) | ||
let responseText = '' | ||
const stream = axiosResponse.data // 请求被取消之后变成 undefined | ||
const status = axiosResponse.status | ||
if (status === 200) { | ||
let hasErr = false | ||
let err: IErnieBotResponseErr | ||
stream.on('data', (buf: Buffer) => { | ||
try { | ||
const dataArr = buf.toString().split('\n') | ||
let onDataPieceText = '' | ||
for (const dataStr of dataArr) { | ||
// split 之后的空行,或者结束通知 | ||
if (dataStr[0] === '{') throw dataStr | ||
if (dataStr.indexOf('data: ') !== 0) continue | ||
const parsedData = JSON.parse(dataStr.slice(6)) // [data: ] | ||
const pieceText = parsedData.result || '' | ||
onDataPieceText += pieceText | ||
} | ||
opts.onProgress(onDataPieceText, buf.toString()) | ||
responseText += onDataPieceText | ||
} catch (e) { | ||
const dataStr = buf.toString() | ||
const parsedData = JSON.parse(dataStr) // [data: ] | ||
hasErr = true | ||
err = parsedData | ||
} | ||
}) | ||
stream.on('end', async () => { | ||
opts.onEnd({ | ||
success: !hasErr, | ||
err, | ||
text: responseText, | ||
}) | ||
}) | ||
} else { | ||
opts.onEnd({ | ||
success: false, | ||
err: String(axiosResponse.data), | ||
text: '', | ||
}) | ||
} | ||
} catch (error: any) { | ||
opts.onEnd({ | ||
success: false, | ||
err: String(error.message), | ||
text: '', | ||
}) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
export interface IErnieBotAccessTokenErr { | ||
error_description: string | ||
error: string | ||
} | ||
export interface IErnieBotAccessToken { | ||
refresh_token: string | ||
expires_in: number | ||
session_key: string | ||
access_token: string | ||
scope: string | ||
session_secret: string | ||
} | ||
export interface IErnieBotUserMessage { | ||
role: 'user' | ||
content: string | ||
} | ||
export interface IErnieBotAssistantMessage { | ||
role: 'assistant' | ||
content: string | ||
} | ||
export type TErnieBotCommonMessage = | ||
| IErnieBotUserMessage | ||
| IErnieBotAssistantMessage | ||
|
||
export interface IErnieBotResponseErr { | ||
error_code: number | ||
error_msg: string | ||
id: string | ||
} | ||
export interface IErnieBotOnEndOpts { | ||
success: boolean | ||
err?: IErnieBotResponseErr | string | ||
text: string | ||
} | ||
export interface IErnieBotSendMessageOpts { | ||
initialMessages: TErnieBotCommonMessage[] | ||
onProgress: (t: string, raw: string) => void | ||
onEnd: (opts: IErnieBotOnEndOpts) => void | ||
model?: string | ||
} |