forked from xiangsx/gpt4free-ts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
132 lines (109 loc) · 3.92 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import es from 'event-stream';
import {PassThrough, Stream} from 'stream';
import * as crypto from 'crypto';
import {v4} from "uuid";
import {encoding_for_model} from '@dqbd/tiktoken'
const en = encoding_for_model("gpt-3.5-turbo");
type eventFunc = (eventName: string, data: string) => void;
export function toEventCB(arr: Uint8Array, emit: eventFunc) {
const pt = new PassThrough();
pt.write(arr)
pt.pipe(es.split(/\r?\n\r?\n/)) //split stream to break on newlines
.pipe(es.map(async function (chunk: any, cb: Function) { //turn this async function into a stream
const [eventStr, dataStr] = (chunk as any).split(/\r?\n/)
const event = eventStr.replace(/event: /, '');
const data = dataStr.replace(/data: /, '');
emit(event, data);
cb(null, {data, event});
}))
}
export function toEventStream(arr: Uint8Array): Stream {
const pt = new PassThrough();
pt.write(arr)
return pt;
}
export function md5(str: string): string {
return crypto.createHash('md5').update(str).digest('hex');
}
export function randomStr(): string {
return v4().split('-').join('').slice(-6);
}
export function parseJSON<T>(str: string, defaultObj: T): T {
try {
return JSON.parse(str)
} catch (e) {
console.log(str);
return defaultObj;
}
}
export function encryptWithAes256Cbc(data: string, key: string): string {
const hash = crypto.createHash('sha256').update(key).digest();
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', hash, iv);
let encryptedData = cipher.update(data, 'utf-8', 'hex');
encryptedData += cipher.final('hex');
return iv.toString('hex') + encryptedData;
}
export async function sleep(duration: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => resolve(), duration);
})
}
export function shuffleArray<T>(array: T[]): T[] {
const shuffledArray = [...array];
for (let i = shuffledArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
}
return shuffledArray;
}
export type ErrorData = { error: string; };
export type MessageData = { content: string };
export type DoneData = MessageData;
export enum Event {
error = 'error',
message = 'message',
done = 'done',
}
export type Data<T extends Event> =
T extends Event.error ? ErrorData :
T extends Event.message ? MessageData :
T extends Event.done ? DoneData : any;
export type DataCB<T extends Event> = (event: T, data: Data<T>) => void
export class EventStream {
private readonly pt: PassThrough = new PassThrough();
constructor() {
this.pt.setEncoding('utf-8');
}
write<T extends Event>(event: T, data: Data<T>) {
this.pt.write(`event: ${event}\n`, 'utf-8');
this.pt.write(`data: ${JSON.stringify(data)}\n\n`, 'utf-8');
}
stream() {
return this.pt;
}
end(cb?: () => void) {
this.pt.end(cb)
}
read(dataCB: DataCB<Event>, closeCB: () => void) {
this.pt.setEncoding('utf-8');
this.pt.pipe(es.split('\n\n')).pipe(es.map(async (chunk: any, cb: any) => {
const res = chunk.toString()
if (!res) {
return;
}
const [eventStr, dataStr] = res.split('\n');
const event: Event = eventStr.replace('event: ', '');
if (!(event in Event)) {
dataCB(Event.error, {error: `EventStream data read failed, not support event ${event}`});
return;
}
const data = parseJSON(dataStr.replace('data: ', ''), {} as Data<Event>);
return dataCB(event, data);
}))
this.pt.on("close", closeCB)
}
}
export const getTokenSize = (str: string) => {
return en.encode(str).length;
};