diff --git a/index.ts b/index.ts index 03b78e5..f9eb8c2 100644 --- a/index.ts +++ b/index.ts @@ -1,10 +1,21 @@ -import Koa from 'koa'; +import Koa, {Context, Next} from 'koa'; import Router from 'koa-router' import bodyParser from 'koa-bodyparser'; import {ChatModelFactory, Model} from "./model"; const app = new Koa(); const router = new Router(); +const errorHandler = async (ctx: Context, next: Next) => { + try { + await next(); + } catch (err:any) { + ctx.response.status = err.status || 500; + ctx.response.body = { + message: err.message, + }; + } +}; +app.use(errorHandler); app.use(bodyParser()); const chatModel = new ChatModelFactory({proxy: process.env.https_proxy || process.env.http_proxy}); @@ -14,7 +25,7 @@ interface AskReq { } router.get('/ask', async (ctx) => { - const {prompt, model = Model.Forefront} = ctx.query as unknown as AskReq; + const {prompt, model = Model.Forefront, ...options} = ctx.query as unknown as AskReq; if (!prompt) { ctx.body = 'please input prompt'; return; @@ -24,12 +35,12 @@ router.get('/ask', async (ctx) => { ctx.body = 'Unsupported model'; return; } - const res = await chat.ask({prompt: prompt as string}); + const res = await chat.ask({prompt: prompt as string, options}); ctx.body = res.text; }); router.get('/ask/stream', async (ctx) => { - const {prompt, model = Model.Forefront} = ctx.query as unknown as AskReq; + const {prompt, model = Model.Forefront, ...options} = ctx.query as unknown as AskReq; if (!prompt) { ctx.body = 'please input prompt'; return; @@ -44,12 +55,12 @@ router.get('/ask/stream', async (ctx) => { "Cache-Control": "no-cache", "Connection": "keep-alive", }); - const res = await chat.askStream({prompt: prompt as string}); + const res = await chat.askStream({prompt: prompt as string, options}); ctx.body = res.text; }) app.use(router.routes()); -app.listen(3000,()=>{ +app.listen(3000, () => { console.log("Now listening: 127.0.0.1:3000"); }); diff --git a/model/base.ts b/model/base.ts index db40a0f..276fbd9 100644 --- a/model/base.ts +++ b/model/base.ts @@ -16,21 +16,14 @@ export interface ResponseStream { export interface Request { prompt: string; - history?: HistoryItem[]; options?: any; } -export interface HistoryItem { - question?: string; - answer?: string; -} - - export abstract class Chat { - protected proxy: string | undefined; + protected options: ChatOptions | undefined; protected constructor(options?: ChatOptions) { - this.proxy = options?.proxy; + this.options = options; } public abstract ask(req: Request): Promise diff --git a/model/forefront/index.ts b/model/forefront/index.ts index c3ec144..e5474a3 100644 --- a/model/forefront/index.ts +++ b/model/forefront/index.ts @@ -13,9 +13,11 @@ interface ForefrontRequest extends Request { options?: { chatId?: string; prompt?: string; - actionType?: string; + actionType?: Action; defaultPersona?: string; - model?: string; + gptmodel?: Model; + // if set true, auto sign up when gpt4 times use up + resignup?: boolean; } } @@ -48,9 +50,15 @@ export enum Action { newGreeting = 'new:greeting', } +export enum Model { + gpt4 = 'gpt-4', + gpt3_5 = 'gpt-3.5-turbo', +} + export class Forefront extends Chat { private client: AxiosInstance | undefined; private session: ForefrontSessionInfo | undefined; + private keepAliveInterval: NodeJS.Timer | undefined = undefined; constructor(options?: ChatOptions) { super(options); @@ -87,7 +95,7 @@ export class Forefront extends Chat { } - public async askStream(req: Request): Promise { + public async askStream(req: ForefrontRequest): Promise { if (!this.client) { await this.initClient(); } @@ -96,9 +104,9 @@ export class Forefront extends Chat { } const { chatId = v4(), - actionType = 'new', + actionType = Action.new, defaultPersona = '607e41fe-95be-497e-8e97-010a59b2e2c0', - model = 'gpt-4', + gptmodel = Model.gpt4, } = req.options || {}; const jsonData = { text: req.prompt, @@ -106,7 +114,7 @@ export class Forefront extends Chat { parentId: chatId, workspaceId: chatId, messagePersona: defaultPersona, - model: model, + model: gptmodel, }; const base64Data = Buffer.from(this.session.userID + defaultPersona + chatId).toString('base64'); const encryptedSignature = encryptWithAes256Cbc(base64Data, this.session.sessionID); @@ -115,11 +123,21 @@ export class Forefront extends Chat { const response = await this.client?.post( 'https://streaming.tenant-forefront-default.knative.chi.coreweave.com/chat', jsonData, { - responseType: 'stream', headers: {'x-signature': encryptedSignature} + responseType: 'stream', headers: { + 'x-signature': encryptedSignature, 'authorization': 'Bearer ' + this.session.token, + } } as AxiosRequestConfig ); return {text: response.data}; - } catch (e) {// session will expire very fast, I cannot know what reason + } catch (e: any) {// session will expire very fast, I cannot know what reason + if (e.response.status === 401) { + if (req.options?.resignup) { + this.client = undefined; + // do not retry auto, avoid loss control + throw new Error('retry again, will sign up again'); + } + throw new Error('try change model to gpt-3.5-turbo or set resignup = true') + } throw e; } } @@ -132,7 +150,6 @@ export class Forefront extends Chat { 'authority': 'chat-server.tenant-forefront-default.knative.chi.coreweave.com', 'accept': '*/*', 'accept-language': 'en,fr-FR;q=0.9,fr;q=0.8,es-ES;q=0.7,es;q=0.6,en-US;q=0.5,am;q=0.4,de;q=0.3', - 'authorization': 'Bearer ' + hisSession.token, 'cache-control': 'no-cache', 'content-type': 'application/json', 'origin': 'https://chat.forefront.ai', @@ -158,8 +175,8 @@ export class Forefront extends Chat { origin: 'https://accounts.forefront.ai', 'user-agent': agent, // Replace with actual random user agent } - if (this.proxy) { - session.proxy = this.proxy; + if (this.options?.proxy) { + session.proxy = this.options.proxy; } const signEmailRes = await session.post('https://clerk.forefront.ai/v1/client/sign_ups?_clerk_js_version=4.38.4', {data: {'email_address': mailAddress}}); @@ -193,6 +210,16 @@ export class Forefront extends Chat { const token = (loginRes.data as any).response.sessions[0].last_active_token.jwt; const sessionID = (loginRes.data as any).response.sessions[0].id const userID = (loginRes.data as any).response.sessions[0].user.id + this.keepAliveInterval = setInterval(async () => { + try { + const keepAliveRes = await session.post(`https://clerk.forefront.ai/v1/client/sessions/${sessionID}/tokens?_clerk_js_version=4.39.0`); + if (this.session) { + this.session.token = (keepAliveRes.data as any).jwt as string; + } + } catch (e) { + console.error(e); + } + }, 30 * 1000); return {token, agent, sessionID, userID}; } } diff --git a/model/you/index.ts b/model/you/index.ts index ceb3407..2c58e50 100644 --- a/model/you/index.ts +++ b/model/you/index.ts @@ -69,8 +69,8 @@ export class You extends Chat { super(props); this.session = new tlsClient.Session({clientIdentifier: 'chrome_108'}); this.session.headers = this.getHeaders(); - if (this.proxy) { - this.session.proxy = this.proxy; + if (this.options?.proxy) { + this.session.proxy = this.options.proxy; } }