| |
| |
| |
| |
| 'use strict'; |
|
|
| const {AI, genericFixes, DangerChars, encodeDataJSON, indexOfAny, cleanJSON, checkResErr} = require('./clewd-utils'), Decoder = new TextDecoder; |
|
|
| class ClewdStream extends TransformStream { |
| constructor(opts, logger) { |
| super({ |
| transform: (chunk, controller) => { |
| this.#handle(chunk, controller); |
| }, |
| flush: controller => { |
| this.#done(controller); |
| } |
| }); |
| this.#logger = logger; |
| this.#version = opts.version; |
| this.#config = opts.config; |
| this.#model = opts.model; |
| this.#streaming = opts.streaming; |
| this.#minSize = opts.minSize || 8; |
| this.#abortControl = opts.abortControl; |
| this.#source = opts.source; |
| } |
| #source=void 0; |
| #ended=false; |
| #streaming=void 0; |
| #minSize=void 0; |
| #compOK=''; |
| #compRaw=''; |
| #logger=void 0; |
| #version=void 0; |
| #config=void 0; |
| #abortControl=void 0; |
| #model=void 0; |
| #compAll=[]; |
| #recvLength=0; |
| #stopLoc=void 0; |
| #stopReason=void 0; |
| #hardCensor=false; |
| #impersonated=false; |
| #nochange=false; |
| get size() { |
| return this.#recvLength; |
| } |
| get total() { |
| return this.#compAll.length; |
| } |
| get censored() { |
| return this.#hardCensor; |
| } |
| get impersonated() { |
| return this.#impersonated; |
| } |
| |
| get nochange() { |
| return this.#nochange; |
| } |
| |
| empty() { |
| this.#compOK = this.#compRaw = ''; |
| this.#compAll = []; |
| this.#recvLength = 0; |
| } |
| #collectBuf() { |
| const valid = [ ...this.#compOK ], selection = valid.splice(0, Math.min(this.#minSize, valid.length)).join(''); |
| this.#compOK = valid.join(''); |
| return selection; |
| } |
| #err(err, controller) { |
| this.#logger?.write(JSON.stringify(err, null, 4)); |
| const message = `## ${this.#version}\n**${this.#model} error**:\n${err.status || err.code || err.type}\n\n\`\`\`${err.message}\`\`\`\n\nFAQ: https://rentry.org/teralomaniac_clewd`; |
| this.#nochange = true; |
| this.#enqueue(this.#build(message), controller); |
| return this.#endEarly(controller); |
| } |
| #build(selection) { |
| this.#logger?.write(selection); |
| const completion = this.#streaming ? { |
| choices: [ { |
| delta: { |
| content: genericFixes(selection) |
| } |
| } ] |
| } : { |
| choices: [ { |
| message: { |
| content: genericFixes(selection) |
| } |
| } ] |
| }; |
| return this.#streaming ? encodeDataJSON(completion) : JSON.stringify(completion); |
| } |
| #enqueue(selection, controller) { |
| this.#ended || controller.enqueue(selection); |
| } |
| #print() {} |
| async #done(controller) { |
| this.#compRaw.length > 0 && await this.#parseBuf(this.#compRaw, controller); |
| this.#streaming ? this.#compOK.length > 0 && this.#enqueue(this.#build(this.#compOK), controller) : this.#enqueue(this.#build(this.#compAll.join('')), controller); |
| this.#compAll?.[0] === Buffer.from([ 73, 32, 97, 112, 111, 108, 111, 103, 105, 122, 101, 44, 32, 98, 117, 116, 32, 73, 32, 119, 105, 108, 108, 32, 110, 111, 116, 32, 112, 114, 111, 118, 105, 100, 101, 32, 97, 110, 121, 32, 114, 101, 115, 112, 111, 110, 115, 101, 115, 32, 116, 104, 97, 116, 32, 118, 105, 111, 108, 97, 116, 101, 32, 65, 110, 116, 104, 114, 111, 112, 105, 99, 39, 115, 32, 65, 99, 99, 101, 112, 116, 97, 98, 108, 101, 32, 85, 115, 101, 32, 80, 111, 108, 105, 99, 121, 32, 111, 114, 32, 99, 111, 117, 108, 100, 32, 112, 114, 111, 109, 111, 116, 101, 32, 104, 97, 114, 109, 46 ]).toString() && (this.#hardCensor = true); |
| if (!this.#ended && 0 === this.total) { |
| const err = `## ${this.#version}\n**error**:\n\n\`\`\`Received no valid replies at all\`\`\`\n\nFAQ: https://rentry.org/teralomaniac_clewd`; |
| this.#nochange = true; |
| this.#enqueue(this.#build(err), controller); |
| } |
| this.#streaming && this.#enqueue('data: [DONE]\n\n', controller); |
| this.#print(); |
| this.#ended = true; |
| } |
| #endEarly(controller) { |
| if (!this.#ended) { |
| this.#streaming && this.#enqueue('data: [DONE]\n\n', controller); |
| this.#config.Settings.Superfetch && this.#source.rape(); |
| this.#abortControl.abort(); |
| controller.terminate(); |
| this.#print(); |
| this.#ended = true; |
| } |
| } |
| #impersonationCheck(reply, controller) { |
| const fakeAny = indexOfAny(reply); |
| if (fakeAny > -1) { |
| this.#impersonated = true; |
| if (this.#config.Settings.PreventImperson) { |
| const selection = reply.substring(0, fakeAny); |
| this.#enqueue(this.#build(selection), controller); |
| this.#endEarly(controller); |
| } |
| } |
| } |
| async #handle(chunk, controller) { |
| if ('string' != typeof chunk) { |
| this.#recvLength += chunk.byteLength; |
| chunk = Decoder.decode(chunk, {'stream': true}); |
| } else { |
| this.#recvLength += Buffer.byteLength(chunk); |
| } |
| this.#compRaw += chunk.replace(/event: (completion|ping)\s*|\r/gi,''); |
| const substr = this.#compRaw.split('\n\n'), lastMsg = substr.length - 1; |
| 0 !== substr[lastMsg].length ? this.#compRaw = substr[lastMsg] : this.#compRaw = ''; |
| for (let i = 0; i < lastMsg; i++) { |
| await this.#parseBuf(substr[i], controller); |
| } |
| } |
| async #parseBuf(json, controller) { |
| if (!json) { |
| return; |
| } |
| if (this.#ended) { |
| return; |
| } |
| let parsed, delayChunk; |
| try { |
| parsed = JSON.parse(cleanJSON(json)); |
| if (parsed.error) { |
| const err = await checkResErr(JSON.stringify({ |
| error: { |
| ...parsed.error |
| }, |
| status: this.#source.status, |
| superfetch: this.#source.superfetch |
| }), false, false); |
| delete err.stack; |
| return this.#err(err, controller); |
| } |
| if (parsed.completion) { |
| parsed.completion = genericFixes(parsed.completion); |
| this.#compOK += parsed.completion; |
| this.#compAll.push(parsed.completion); |
| delayChunk = DangerChars.some((char => this.#compOK.endsWith(char) || parsed.completion.startsWith(char))); |
| } |
| !this.#stopLoc && parsed.stop && (this.#stopLoc = parsed.stop.replace(/\n/g, '\\n')); |
| !this.#stopReason && parsed.stop_reason && (this.#stopReason = parsed.stop_reason); |
| if (this.#streaming) { |
| delayChunk && this.#impersonationCheck(this.#compOK, controller); |
| for (;!delayChunk && this.#compOK.length >= this.#minSize; ) { |
| const selection = this.#collectBuf(); |
| this.#enqueue(this.#build(selection), controller); |
| } |
| } else { |
| delayChunk && this.#impersonationCheck(this.#compAll.join(''), controller); |
| } |
| } catch (err) {} |
| } |
| } |
|
|
| module.exports = ClewdStream; |