mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-04-14 02:42:08 +02:00
Refactor event parsing
This commit is contained in:
parent
51b3b8bfaa
commit
8176e09d4a
@ -77,14 +77,14 @@ class EventSourceStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Like the default one, but multiplies the events by the number of letters in the event data.
|
|
||||||
*/
|
|
||||||
export class SmoothEventSourceStream extends EventSourceStream {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const defaultDelayMs = 20;
|
const defaultDelayMs = 20;
|
||||||
const punctuationDelayMs = 500;
|
const punctuationDelayMs = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a delay based on the character.
|
||||||
|
* @param {string} s The character.
|
||||||
|
* @returns {number} The delay in milliseconds.
|
||||||
|
*/
|
||||||
function getDelay(s) {
|
function getDelay(s) {
|
||||||
if (!s) {
|
if (!s) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -100,6 +100,129 @@ export class SmoothEventSourceStream extends EventSourceStream {
|
|||||||
|
|
||||||
return defaultDelayMs;
|
return defaultDelayMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the stream data and returns the parsed data and the chunk to be sent.
|
||||||
|
* @param {object} json The JSON data.
|
||||||
|
* @returns {AsyncGenerator<{data: object, chunk: string} | null>} The parsed data and the chunk to be sent.
|
||||||
|
*/
|
||||||
|
async function* parseStreamData(json) {
|
||||||
|
// Claude
|
||||||
|
if (typeof json.delta === 'object') {
|
||||||
|
if (typeof json.delta.text === 'string' && json.delta.text.length > 0) {
|
||||||
|
for (let i = 0; i < json.delta.text.length; i++) {
|
||||||
|
const str = json.delta.text[i];
|
||||||
|
yield {
|
||||||
|
data: { ...json, delta: { text: str } },
|
||||||
|
chunk: str,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// MakerSuite
|
||||||
|
else if (Array.isArray(json.candidates)) {
|
||||||
|
for (let i = 0; i < json.candidates.length; i++) {
|
||||||
|
if (typeof json.candidates[i].content === 'string' && json.candidates[i].content.length > 0) {
|
||||||
|
for (let j = 0; j < json.candidates[i].content.length; j++) {
|
||||||
|
const str = json.candidates[i].content[j];
|
||||||
|
const candidatesClone = structuredClone(json.candidates[i]);
|
||||||
|
candidatesClone[i].content = str;
|
||||||
|
yield {
|
||||||
|
data: { ...json, candidates: candidatesClone },
|
||||||
|
chunk: str,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NovelAI / KoboldCpp Classic
|
||||||
|
else if (typeof json.token === 'string' && json.token.length > 0) {
|
||||||
|
for (let i = 0; i < json.token.length; i++) {
|
||||||
|
const str = json.token[i];
|
||||||
|
yield {
|
||||||
|
data: { ...json, token: str },
|
||||||
|
chunk: str,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// llama.cpp?
|
||||||
|
else if (typeof json.content === 'string' && json.content.length > 0) {
|
||||||
|
for (let i = 0; i < json.content.length; i++) {
|
||||||
|
const str = json.content[i];
|
||||||
|
yield {
|
||||||
|
data: { ...json, content: str },
|
||||||
|
chunk: str,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OpenAI-likes
|
||||||
|
else if (Array.isArray(json.choices)) {
|
||||||
|
const isNotPrimary = json?.choices?.[0]?.index > 0;
|
||||||
|
if (isNotPrimary || json.choices.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (typeof json.choices[0].delta === 'object') {
|
||||||
|
if (typeof json.choices[0].delta.text === 'string' && json.choices[0].delta.text.length > 0) {
|
||||||
|
for (let j = 0; j < json.choices[0].delta.text.length; j++) {
|
||||||
|
const str = json.choices[0].delta.text[j];
|
||||||
|
const choiceClone = structuredClone(json.choices[0]);
|
||||||
|
choiceClone.delta.text = str;
|
||||||
|
const choices = [choiceClone];
|
||||||
|
yield {
|
||||||
|
data: { ...json, choices },
|
||||||
|
chunk: str,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) {
|
||||||
|
for (let j = 0; j < json.choices[0].delta.content.length; j++) {
|
||||||
|
const str = json.choices[0].delta.content[j];
|
||||||
|
const choiceClone = structuredClone(json.choices[0]);
|
||||||
|
choiceClone.delta.content = str;
|
||||||
|
const choices = [choiceClone];
|
||||||
|
yield {
|
||||||
|
data: { ...json, choices },
|
||||||
|
chunk: str,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof json.choices[0].message === 'object') {
|
||||||
|
if (typeof json.choices[0].message.content === 'string' && json.choices[0].message.content.length > 0) {
|
||||||
|
for (let j = 0; j < json.choices[0].message.content.length; j++) {
|
||||||
|
const str = json.choices[0].message.content[j];
|
||||||
|
const choiceClone = structuredClone(json.choices[0]);
|
||||||
|
choiceClone.message.content = str;
|
||||||
|
const choices = [choiceClone];
|
||||||
|
yield {
|
||||||
|
data: { ...json, choices },
|
||||||
|
chunk: str,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof json.choices[0].text === 'string' && json.choices[0].text.length > 0) {
|
||||||
|
for (let j = 0; j < json.choices[0].text.length; j++) {
|
||||||
|
const str = json.choices[0].text[j];
|
||||||
|
const choiceClone = structuredClone(json.choices[0]);
|
||||||
|
choiceClone.text = str;
|
||||||
|
const choices = [choiceClone];
|
||||||
|
yield {
|
||||||
|
data: { ...json, choices },
|
||||||
|
chunk: str,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like the default one, but multiplies the events by the number of letters in the event data.
|
||||||
|
*/
|
||||||
|
export class SmoothEventSourceStream extends EventSourceStream {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
let lastStr = '';
|
let lastStr = '';
|
||||||
const transformStream = new TransformStream({
|
const transformStream = new TransformStream({
|
||||||
async transform(chunk, controller) {
|
async transform(chunk, controller) {
|
||||||
@ -109,119 +232,19 @@ export class SmoothEventSourceStream extends EventSourceStream {
|
|||||||
const json = JSON.parse(data);
|
const json = JSON.parse(data);
|
||||||
|
|
||||||
if (!json) {
|
if (!json) {
|
||||||
controller.enqueue(event);
|
lastStr = '';
|
||||||
return;
|
return controller.enqueue(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Claude
|
for await (const parsed of parseStreamData(json)) {
|
||||||
if (typeof json.delta === 'object') {
|
if (!parsed) {
|
||||||
if (typeof json.delta.text === 'string' && json.delta.text.length > 0) {
|
lastStr = '';
|
||||||
for (let i = 0; i < json.delta.text.length; i++) {
|
return controller.enqueue(event);
|
||||||
await delay(getDelay(lastStr));
|
|
||||||
const str = json.delta.text[i];
|
|
||||||
controller.enqueue(new MessageEvent(event.type, { data: JSON.stringify({ ...json, delta: { text: str } }) }));
|
|
||||||
lastStr = str;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
controller.enqueue(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// MakerSuite
|
|
||||||
else if (Array.isArray(json.candidates)) {
|
|
||||||
for (let i = 0; i < json.candidates.length; i++) {
|
|
||||||
if (typeof json.candidates[i].content === 'string' && json.candidates[i].content.length > 0) {
|
|
||||||
for (let j = 0; j < json.candidates[i].content.length; j++) {
|
|
||||||
await delay(getDelay(lastStr));
|
|
||||||
const str = json.candidates[i].content[j];
|
|
||||||
const candidatesClone = structuredClone(json.candidates[i]);
|
|
||||||
candidatesClone[i].content = str;
|
|
||||||
controller.enqueue(new MessageEvent(event.type, { data: JSON.stringify({ ...json, candidates: candidatesClone }) }));
|
|
||||||
lastStr = str;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
controller.enqueue(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// NovelAI / KoboldCpp Classic
|
|
||||||
else if (typeof json.token === 'string' && json.token.length > 0) {
|
|
||||||
for (let i = 0; i < json.token.length; i++) {
|
|
||||||
await delay(getDelay(lastStr));
|
|
||||||
const str = json.token[i];
|
|
||||||
controller.enqueue(new MessageEvent(event.type, { data: JSON.stringify({ ...json, token: str }) }));
|
|
||||||
lastStr = str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// llama.cpp?
|
|
||||||
else if (typeof json.content === 'string' && json.content.length > 0) {
|
|
||||||
for (let i = 0; i < json.content.length; i++) {
|
|
||||||
await delay(getDelay(lastStr));
|
|
||||||
const str = json.content[i];
|
|
||||||
controller.enqueue(new MessageEvent(event.type, { data: JSON.stringify({ ...json, content: str }) }));
|
|
||||||
lastStr = str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// OpenAI-likes
|
|
||||||
else if (Array.isArray(json.choices)) {
|
|
||||||
const isNotPrimary = json?.choices?.[0]?.index > 0;
|
|
||||||
if (isNotPrimary || json.choices.length === 0) {
|
|
||||||
controller.enqueue(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (typeof json.choices[0].delta === 'object') {
|
|
||||||
if (typeof json.choices[0].delta.text === 'string' && json.choices[0].delta.text.length > 0) {
|
|
||||||
for (let j = 0; j < json.choices[0].delta.text.length; j++) {
|
|
||||||
await delay(getDelay(lastStr));
|
|
||||||
const str = json.choices[0].delta.text[j];
|
|
||||||
const choiceClone = structuredClone(json.choices[0]);
|
|
||||||
choiceClone.delta.text = str;
|
|
||||||
const choices = [choiceClone];
|
|
||||||
controller.enqueue(new MessageEvent(event.type, { data: JSON.stringify({ ...json, choices }) }));
|
|
||||||
lastStr = str;
|
|
||||||
}
|
|
||||||
} else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) {
|
|
||||||
for (let j = 0; j < json.choices[0].delta.content.length; j++) {
|
|
||||||
await delay(getDelay(lastStr));
|
|
||||||
const str = json.choices[0].delta.content[j];
|
|
||||||
const choiceClone = structuredClone(json.choices[0]);
|
|
||||||
choiceClone.delta.content = str;
|
|
||||||
const choices = [choiceClone];
|
|
||||||
controller.enqueue(new MessageEvent(event.type, { data: JSON.stringify({ ...json, choices }) }));
|
|
||||||
lastStr = str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
controller.enqueue(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (typeof json.choices[0].message === 'object') {
|
|
||||||
if (typeof json.choices[0].message.content === 'string' && json.choices[0].message.content.length > 0) {
|
|
||||||
for (let j = 0; j < json.choices[0].message.content.length; j++) {
|
|
||||||
await delay(getDelay(lastStr));
|
await delay(getDelay(lastStr));
|
||||||
const str = json.choices[0].message.content[j];
|
controller.enqueue(new MessageEvent(event.type, { data: JSON.stringify(parsed.data) }));
|
||||||
const choiceClone = structuredClone(json.choices[0]);
|
lastStr = parsed.chunk;
|
||||||
choiceClone.message.content = str;
|
|
||||||
const choices = [choiceClone];
|
|
||||||
controller.enqueue(new MessageEvent(event.type, { data: JSON.stringify({ ...json, choices }) }));
|
|
||||||
lastStr = str;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
controller.enqueue(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (typeof json.choices[0].text === 'string' && json.choices[0].text.length > 0) {
|
|
||||||
for (let j = 0; j < json.choices[0].text.length; j++) {
|
|
||||||
await delay(getDelay(lastStr));
|
|
||||||
const str = json.choices[0].text[j];
|
|
||||||
const choiceClone = structuredClone(json.choices[0]);
|
|
||||||
choiceClone.text = str;
|
|
||||||
const choices = [choiceClone];
|
|
||||||
controller.enqueue(new MessageEvent(event.type, { data: JSON.stringify({ ...json, choices }) }));
|
|
||||||
lastStr = str;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
controller.enqueue(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
controller.enqueue(event);
|
controller.enqueue(event);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user