Skip to content

Commit

Permalink
feat: 完善并简化 OPENAI 调用
Browse files Browse the repository at this point in the history
  • Loading branch information
weaigc committed Sep 24, 2023
1 parent 34616a4 commit 3267edc
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 15 deletions.
72 changes: 72 additions & 0 deletions OPENAI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Bingo OpenAI

为了方便将 `new bing` 接入到其他 `gpt` 类的项目,现开放 `OpenAI` 格式的 `API` 接口供大家调用。

## 接口说明
### 入参
* url: /openai/chat/completions (PS: 为了方便兼容不同的项目,所有以 `/completions` 结尾的请求都会被支持)
* Content-Type: application/json
* 参数说明
* messages 输入的消息列表,完整格式参见 https://platform.openai.com/docs/guides/gpt/chat-completions-api
* model 模型名称(此字段被用于指定 new bing 风格,参数为 Creative、Balanced、Precise 中的一种)
* stream 是否使用流式输出,默认为

### 出参
* Content-Type: application/json 或者 text/event-stream
* 参数说明
* choices 返回的消息内容,完整格式参见 https://platform.openai.com/docs/guides/gpt/chat-completions-response-format
* id 会话 ID,如需保持上下文,则需要传入此参数,否则会丢失上下文

### 示例
以下以 `curl` 为例
```
```

### 限制
* 暂时只支持聊天(`/completions`)接口,其他接口如有需求,请在 [issue](https://github.com/weaigc/bingo/issues) 提出
* 受 new bing 限制,暂不支持自定义历史记录

## 调用方式
除了使用 HTTP POST 请求来调用之外,你也可以使用自己熟悉的方式来调用 new bing,如 python 的 openai 库或其它语言的同名包。下面例举一下 Python 和 Node.js 的用法

### Python
```
import openai
openai.api_key = "dummy"
openai.api_base = "https://hf4all-bingo-api.hf.space" # 这里可以改为你自己部署的服务,bingo 服务版本需要 >= 0.9.0
# create a chat completion
chat_completion = openai.ChatCompletion.create(model="Creative", messages=[{"role": "user", "content": "Hello"}])
# print the completion
print(chat_completion.choices[0].message.content)
```
> 更多使用说明参考 https://github.com/openai/openai-python
### Node.js
```
import OpenAI from 'openai';
const openai = new OpenAI({
baseURL: 'https://hf4all-bingo-api.hf.space' // 这里可以改为你自己部署的服务,bingo 服务版本需要 >= 0.9.0
});
async function main() {
const stream = await openai.chat.completions.create({
model: 'Creative',
messages: [{ role: 'user', content: 'Hello' }],
stream: true,
});
for await (const part of stream) {
process.stdout.write(part.choices[0]?.delta?.content || '');
}
}
main();
```
> 更多使用说明参考 https://github.com/openai/openai-node

## 在线演示

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Bingo,一个让你呼吸顺畅 New Bing。
- 支持持续语音对话
- 支持免账号使用
- 完全免费
- 支持 OpenAI 方式调用 [使用文档](./OPENAI.md)

## RoadMap

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "bingo",
"version": "0.8.0",
"version": "0.9.0",
"private": true,
"main": "./cloudflare/cli.js",
"scripts": {
"dev": "cross-env DEBUG=bingo* next dev --hostname 0.0.0.0",
"dev": "cross-env DEBUG=bingo* NODE_ENV=development node ./server.js",
"build": "next build",
"start": "cross-env NODE_ENV=production node ./server.js",
"lint": "next lint"
Expand Down
7 changes: 5 additions & 2 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@ app.prepare().then(() => {

async function handleRequest(req, res) {
try {
const { method } = req
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname } = parsedUrl
const { pathname, query } = parsedUrl

if (pathname === '/api/counts') {
server.getConnections(function(error, count) {
server.getConnections(function (error, count) {
res.end(String(count))
})
} else if (pathname.endsWith('/completions')) {
await app.render(req, res, '/api/openai/chat/completions', query)
} else {
await handle(req, res, parsedUrl)
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/bots/bing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export class BingWebBot {
let resp: ConversationResponse | undefined
try {
const search = conversationId ? `?conversationId=${encodeURIComponent(conversationId)}` : ''
const response = await fetch(`${this.endpoint}/api/create${search}`, { method: 'POST', headers, redirect: 'error', mode: 'cors', credentials: 'include' })
const response = await fetch(`${this.endpoint}/api/create${search}`, { method: 'POST', headers, mode: 'cors', credentials: 'include' })
if (response.status === 404) {
throw new ChatError('Not Found', ErrorCode.NOTFOUND_ERROR)
}
Expand Down
2 changes: 0 additions & 2 deletions src/pages/api/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (!json?.conversationSignature) {
continue
}
const cookies = [`BING_IP=${headers['x-forwarded-for']}`]

res.setHeader('set-cookie', cookies.map(cookie => `${cookie.trim()}; Max-Age=${86400 * 30}; Path=/;`))
debug('headers', headers)
res.writeHead(200, {
'Content-Type': 'application/json',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,26 @@ function responseOpenAIMessage(content: string, id?: string): APIResponse {
};
}

function getOriginFromHost(host: string) {
const uri = new URL(`http://${host}`)
if (uri.protocol === 'http:' && !/^[0-9.:]+$/.test(host)) {
uri.protocol = 'https:';
}
return uri.toString().slice(0, -1)
}

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// if (req.method !== 'POST') return res.status(403).end()
if (req.method !== 'POST') return res.status(200).end('ok')
await NextCors(req, res, {
// Options
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
origin: '*',
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
optionsSuccessStatus: 200,
});
const { prompt, stream, model } = parseOpenAIMessage(req.body);
console.log('prompt', prompt, stream, process.env.PORT)
let { id } = req.body
const chatbot = new BingWebBot({
endpoint: `http://127.0.0.1:${process.env.PORT}`,
cookie: `BING_IP=${process.env.BING_IP}`
endpoint: getOriginFromHost(req.headers.host || '127.0.0.1:3000'),
})
id ||= JSON.stringify(chatbot.createConversation())

Expand All @@ -96,10 +102,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (stream && lastLength !== lastText.length) {
res.write(`data: ${JSON.stringify(responseOpenAIMessage(lastText.slice(lastLength), id))}\n\n`)
res.flushHeaders()

lastLength = lastText.length
}
} else if (event.type === 'ERROR') {
res.write(`data: ${JSON.stringify(responseOpenAIMessage(`${event.error}`, id))}\n\n`)
res.write(`data: ${JSON.stringify(responseOpenAIMessage(`\n\n${event.error}`, id))}\n\n`)
lastText += '\n\n' + event.error
res.flushHeaders()
} else if (event.type === 'DONE') {
if (stream) {
Expand Down
2 changes: 1 addition & 1 deletion tests/openai-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import OpenAI from 'openai';

const openai = new OpenAI({
apiKey: 'dummy',
baseURL: 'https://hf4all-bingo-api.hf.space/api/v1' // 这里改成你自己部署的服务地址
baseURL: 'http://127.0.0.1:3000' // 这里改成你自己部署的服务地址
});

async function start() {
Expand Down
2 changes: 1 addition & 1 deletion tests/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import OpenAI from 'openai';

const openai = new OpenAI({
apiKey: 'dummy',
baseURL: 'https://hf4all-bingo-api.hf.space/api/v1' // 这里改成你自己部署的服务地址
baseURL: 'http://127.0.0.1:3000' // 这里改成你自己部署的服务地址
});

async function start() {
Expand Down

0 comments on commit 3267edc

Please sign in to comment.