File size: 3,272 Bytes
00414e6 |
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 |
import { logDebug } from '../logger.js';
export class OpenAIResponseTransformer {
constructor(model, requestId) {
this.model = model;
this.requestId = requestId || `chatcmpl-${Date.now()}`;
this.created = Math.floor(Date.now() / 1000);
}
parseSSELine(line) {
if (line.startsWith('event:')) {
return { type: 'event', value: line.slice(6).trim() };
}
if (line.startsWith('data:')) {
const dataStr = line.slice(5).trim();
try {
return { type: 'data', value: JSON.parse(dataStr) };
} catch (e) {
return { type: 'data', value: dataStr };
}
}
return null;
}
transformEvent(eventType, eventData) {
logDebug(`Target OpenAI event: ${eventType}`);
if (eventType === 'response.created') {
return this.createOpenAIChunk('', 'assistant', false);
}
if (eventType === 'response.in_progress') {
return null;
}
if (eventType === 'response.output_text.delta') {
const text = eventData.delta || eventData.text || '';
return this.createOpenAIChunk(text, null, false);
}
if (eventType === 'response.output_text.done') {
return null;
}
if (eventType === 'response.done') {
const status = eventData.response?.status;
let finishReason = 'stop';
if (status === 'completed') {
finishReason = 'stop';
} else if (status === 'incomplete') {
finishReason = 'length';
}
const finalChunk = this.createOpenAIChunk('', null, true, finishReason);
const done = this.createDoneSignal();
return finalChunk + done;
}
return null;
}
createOpenAIChunk(content, role = null, finish = false, finishReason = null) {
const chunk = {
id: this.requestId,
object: 'chat.completion.chunk',
created: this.created,
model: this.model,
choices: [
{
index: 0,
delta: {},
finish_reason: finish ? finishReason : null
}
]
};
if (role) {
chunk.choices[0].delta.role = role;
}
if (content) {
chunk.choices[0].delta.content = content;
}
return `data: ${JSON.stringify(chunk)}\n\n`;
}
createDoneSignal() {
return 'data: [DONE]\n\n';
}
async *transformStream(sourceStream) {
let buffer = '';
let currentEvent = null;
try {
for await (const chunk of sourceStream) {
buffer += chunk.toString();
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.trim()) continue;
const parsed = this.parseSSELine(line);
if (!parsed) continue;
if (parsed.type === 'event') {
currentEvent = parsed.value;
} else if (parsed.type === 'data' && currentEvent) {
const transformed = this.transformEvent(currentEvent, parsed.value);
if (transformed) {
yield transformed;
}
}
}
}
if (currentEvent === 'response.done' || currentEvent === 'response.completed') {
yield this.createDoneSignal();
}
} catch (error) {
logDebug('Error in OpenAI stream transformation', error);
throw error;
}
}
}
|