From 3b9719d50614c7e7f615bc9f5c8bf0b01e17e9d8 Mon Sep 17 00:00:00 2001 From: xiang <1984871009@qq.com> Date: Sat, 3 Jun 2023 14:45:28 +0800 Subject: [PATCH] feat: support new mcbbs --- README.md | 28 ++++++++++-- index.ts | 6 +-- model/index.ts | 3 ++ model/mcbbs/index.ts | 100 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 model/mcbbs/index.ts diff --git a/README.md b/README.md index 4172c49..0734c62 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Have implemented models here: If you do not want your website to appear here, please raise an issue and I will remove it immediately. |model|support|status|active time| |--|--|--|--| +|[ai.mcbbs.gq](ai.mcbbs.gq)|gpt3.5|![Active](https://img.shields.io/badge/Active-brightgreen)|after 2023-06-03| |[forefront.ai](forefront.ai)|GPT-4/gpt3.5|![Active](https://img.shields.io/badge/Active-brightgreen)|after 2023-05-12| |[aidream](http://aidream.cloud)|GPT-3.5|![Active](https://img.shields.io/badge/Active-brightgreen)|after 2023-05-12| |[you.com](you.com)|GPT-3.5|![Active](https://img.shields.io/badge/Active-brightgreen)|after 2023-05-12 @@ -67,7 +68,23 @@ docker-compose up --build -d prompt: string; // required ``` -aidread options +#### mcbbs options + +```typescript +interface Message { + role: string; + content: string; +} + +interface options { + parse: string; + messages: string; // attattion messages is Message[] json string + temperature: number; +} + +``` + +#### aidread options ```typescript interface options { @@ -79,7 +96,7 @@ interface options { } ``` -forefront options +#### forefront options ``` chatId?: string; @@ -97,9 +114,14 @@ resignup?: number; // default 0 if set 1, auto sign up when gpt4 times use up ### test now! common request +use curl or input url in explorer ```shell -# test default model aidream +# test default model mcbbs + +curl '127.0.0.1:3000/ask/stream?messages=[{"role":"system","content":"IMPORTANT: You are a virtual assistant powered by the gpt-3.5-turbo model, now time is 2023/6/3 13:42:27}"},{"role":"user","content":"你好\n"},{"role":"assistant","content":"你好!有什么我可以帮助你的吗?"},{"role":"user","content":"写个冒泡排序\n"}]&prompt=test&model=mcbbs&parse=false' + +# test aidream curl "http://127.0.0.1:3000/ask?prompt=hello&model=aidream" # test default model chat.forefront.at diff --git a/index.ts b/index.ts index 103cd31..9a43aac 100644 --- a/index.ts +++ b/index.ts @@ -28,7 +28,7 @@ interface AskReq { } router.get('/ask', async (ctx) => { - const {prompt, model = Model.Forefront, ...options} = ctx.query as unknown as AskReq; + const {prompt, model = Model.Mcbbs, ...options} = ctx.query as unknown as AskReq; if (!prompt) { ctx.body = 'please input prompt'; return; @@ -39,11 +39,11 @@ router.get('/ask', async (ctx) => { return; } const res = await chat.ask({prompt: prompt as string, options}); - ctx.body = res; + ctx.body = res.text; }); router.get('/ask/stream', async (ctx) => { - const {prompt, model = Model.Forefront, ...options} = ctx.query as unknown as AskReq; + const {prompt, model = Model.Mcbbs, ...options} = ctx.query as unknown as AskReq; if (!prompt) { ctx.body = 'please input prompt'; return; diff --git a/model/index.ts b/model/index.ts index 20e6932..0a8c617 100644 --- a/model/index.ts +++ b/model/index.ts @@ -3,6 +3,7 @@ import {You} from "./you"; import {AiDream} from "./aidream"; import {Phind} from "./phind"; import {Forefrontnew} from "./forefront"; +import {Mcbbs} from "./mcbbs"; export enum Model { // define new model here @@ -10,6 +11,7 @@ export enum Model { Forefront = 'forefront', AiDream = 'aidream', Phind = 'phind', + Mcbbs = 'mcbbs', } export class ChatModelFactory { @@ -28,6 +30,7 @@ export class ChatModelFactory { this.modelMap.set(Model.Forefront, new Forefrontnew(this.options)) this.modelMap.set(Model.AiDream, new AiDream(this.options)) this.modelMap.set(Model.Phind, new Phind(this.options)) + this.modelMap.set(Model.Mcbbs, new Mcbbs(this.options)) } get(model: Model): Chat | undefined { diff --git a/model/mcbbs/index.ts b/model/mcbbs/index.ts new file mode 100644 index 0000000..2be80f3 --- /dev/null +++ b/model/mcbbs/index.ts @@ -0,0 +1,100 @@ +import {Chat, ChatOptions, Request, Response, ResponseStream} from "../base"; +import {AxiosInstance, AxiosRequestConfig, CreateAxiosDefaults} from "axios"; +import {CreateAxiosProxy} from "../../utils/proxyAgent"; +import es from "event-stream"; +import {parseJSON} from "../../utils"; +import {Stream} from "stream"; + +interface Message { + role: string; + content: string; +} + +interface RealReq { + messages: Message[]; + stream: boolean; + model: string; + temperature: number; + presence_penalty: number; +} + +export interface McbbsReq extends Request { + options: { + parse: string; + messages: string; + temperature: number; + } +} + +export class Mcbbs extends Chat { + private client: AxiosInstance; + + constructor(options?: ChatOptions) { + super(options); + this.client = CreateAxiosProxy({ + baseURL: 'https://ai.mcbbs.gq/api', + headers: { + 'Content-Type': 'application/json', + "accept": "text/event-stream", + "Cache-Control": "no-cache", + "Proxy-Connection": "keep-alive" + } + } as CreateAxiosDefaults); + } + + public async ask(req: McbbsReq): Promise { + const res = await this.askStream(req) + const result: Response = { + text: '', other: {} + } + return new Promise(resolve => { + res.text.on('data', (data) => { + result.text += data; + }).on('close', () => { + resolve(result); + }) + }) + + } + + public async askStream(req: McbbsReq): Promise { + const { + messages, + temperature = 1, + parse = 'true' + } = req.options; + const data: RealReq = { + stream: true, + messages: JSON.parse(messages), + temperature, + presence_penalty: 2, + model: 'gpt-3.5-turbo' + }; + const res = await this.client.post('/openai/v1/chat/completions', data, { + responseType: 'stream', + } as AxiosRequestConfig); + if (parse === 'false') { + return {text: res.data} + } + return { + text: this.parseData(res.data) + }; + } + + parseData(v: Stream): Stream { + return v.pipe(es.split(/\r?\n\r?\n/)).pipe(es.map(async (chunk: any, cb: any) => { + const dataStr = chunk.replace('data: ', ''); + if (dataStr === '[Done]') { + cb(null, ''); + return; + } + const data = parseJSON(dataStr, {}); + if (!data?.choices) { + cb(null, ''); + return; + } + const [{delta: {content = ""}}] = data.choices; + cb(null, content); + })) + } +}