//@ts-ignore import UserAgent from 'user-agents'; import tlsClient from 'tls-client'; import {Chat, ChatOptions, Request, Response, ResponseStream} from "../base"; import {CreateEmail, TempEmailType, TempMailMessage} from '../../utils/emailFactory'; import axios, {AxiosInstance, AxiosRequestConfig, CreateAxiosDefaults} from "axios"; import {v4} from "uuid"; import es from "event-stream"; import {encryptWithAes256Cbc, parseJSON} from "../../utils"; interface ForefrontRequest extends Request { options?: { chatId?: string; prompt?: string; actionType?: Action; defaultPersona?: string; gptmodel?: Model; // if set true, auto sign up when gpt4 times use up resignup?: string; } } interface ChatCompletionChoice { delta: { content: string; }; index: number; finish_reason: string | null; } interface ChatCompletionChunk { id: string; object: string; created: number; model: string; choices: ChatCompletionChoice[]; } interface ForefrontSessionInfo { agent: string; token: string; sessionID: string; userID: string; } export enum Action { new = 'new', continue = 'continue', 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; private gpt4times: number = 0; constructor(options?: ChatOptions) { super(options); this.client = undefined; this.session = undefined; } public async ask(req: ForefrontRequest): Promise { const res = await this.askStream(req); let text = ''; return new Promise(resolve => { res.text.pipe(es.split(/\r?\n\r?\n/)).pipe(es.map(async (chunk: any, cb: any) => { const str = chunk.replace('data: ', ''); if (!str || str === '[DONE]'.trim()) { cb(null, ''); return; } // const data = parseJSON(str, {}) as ChatCompletionChunk; // if (!data.choices) { // cb(null, ''); // return; // } // const [{delta: {content}}] = data.choices; // cb(null, content); cb(null, str); })).on('data', (data) => { if (!data) { return; } text += data; }).on('close', () => { resolve({text, other: res.other}); }) }) } public async askStream(req: ForefrontRequest): Promise { if (!this.client) { await this.initClient(); } if (!this.client || !this.session) { throw new Error('hava not created account'); } const { chatId = v4(), actionType = Action.new, defaultPersona = '607e41fe-95be-497e-8e97-010a59b2e2c0', gptmodel = Model.gpt4, resignup = 0 } = req.options || {}; const jsonData = { text: req.prompt, action: actionType, parentId: chatId, workspaceId: chatId, messagePersona: defaultPersona, model: gptmodel, }; const base64Data = Buffer.from(this.session.userID + defaultPersona + chatId).toString('base64'); const encryptedSignature = encryptWithAes256Cbc(base64Data, this.session.sessionID); try { const response = await this.client?.post( 'https://streaming.tenant-forefront-default.knative.chi.coreweave.com/chat', jsonData, { responseType: 'stream', headers: { 'x-signature': encryptedSignature, 'authorization': 'Bearer ' + this.session.token, } } as AxiosRequestConfig ); const stream = response.data.pipe(es.split(/\r?\n\r?\n/)).pipe(es.map(async (chunk: any, cb: any) => { const str = chunk.replace('data: ', ''); if (!str || str.trim() === '[DONE]') { cb(null, ''); return; } if (str.indexOf('event: error') !== -1) { cb(null, 'GPT-4 rate limit exceeded (>5 messages every 3 hours). Time remaining: 180 minutes; try set resignup=1 in query') if (+resignup) { this.client = undefined; } return; } const data = parseJSON(str, {}) as ChatCompletionChunk; if (!data.choices) { cb(null, ''); return; } const [{delta: {content}}] = data.choices; cb(null, content||''); })) return {text: stream}; } catch (e: any) { if (e.response.status === 401) { if (+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=1') } throw e; } finally { if (req.options?.gptmodel === Model.gpt4) { this.gpt4times += 1; if (this.gpt4times === 5) { if (+resignup) { this.client = undefined; this.gpt4times = 0; } else { throw new Error('try set resignup=1 in query'); } } } } } async initClient() { let hisSession = await this.createToken(); this.session = hisSession; this.client = axios.create({ headers: { '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', 'cache-control': 'no-cache', 'content-type': 'application/json', 'origin': 'https://chat.forefront.ai', 'pragma': 'no-cache', 'referer': 'https://chat.forefront.ai/', 'sec-ch-ua': '"Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'cross-site', 'user-agent': hisSession.agent, } } as CreateAxiosDefaults); } async createToken(): Promise { const mailbox = CreateEmail(TempEmailType.TempEmail44); const mailAddress = await mailbox.getMailAddress(); const agent = new UserAgent().toString(); const session = new tlsClient.Session({clientIdentifier: 'chrome_108'}); session.headers = { origin: 'https://accounts.forefront.ai', 'user-agent': agent, // Replace with actual random user agent } 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}}); const traceToken = (signEmailRes.data as any)?.response?.id; if (!traceToken) { throw new Error('Failed to create account! sign email res parse token failed!'); } const verifyRes = await session.post(`https://clerk.forefront.ai/v1/client/sign_ups/${traceToken}/prepare_verification?_clerk_js_version=4.38.4`, { data: { 'strategy': 'email_link', 'redirect_url': 'https://accounts.forefront.ai/sign-up/verify' }, }) if (verifyRes.text.indexOf('sign_up_attempt') === -1) { throw new Error('forefront create account failed'); } const msgs = (await mailbox.waitMails()) as TempMailMessage[] let validateURL: string | undefined; for (const msg of msgs) { validateURL = msg.content.match(/https:\/\/clerk\.forefront\.ai\/v1\/verify\?token=[^\s"]+/i)?.[0]; if (validateURL) { break; } } if (!validateURL) { throw new Error('Error while obtaining verfication URL!') } const validateRes = await session.get(validateURL) const loginRes = await session.get('https://clerk.forefront.ai/v1/client?_clerk_js_version=4.38.4'); 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); } }, 50 * 1000); return {token, agent, sessionID, userID}; } }