-
Notifications
You must be signed in to change notification settings - Fork 13.1k
chore: Streaming images for pdf transcript #38263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
52afb62
19659a1
d1864fa
c934591
3f81419
57494f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import type { Readable } from 'stream'; | ||
| import { Readable } from 'stream'; | ||
|
|
||
| import { | ||
| ServiceClass, | ||
|
|
@@ -241,8 +241,9 @@ export class OmnichannelTranscript extends ServiceClass implements IOmnichannelT | |
| } | ||
|
|
||
| try { | ||
| const fileBuffer = await uploadService.getFileBuffer({ file: uploadedFile }); | ||
| files.push({ name: file.name, buffer: fileBuffer, extension: uploadedFile.extension }); | ||
| const stream = await uploadService.streamUploadedFile({ file: uploadedFile, imageResizeOpts: { width: 400, height: 240 } }); | ||
|
|
||
| files.push({ name: file.name, buffer: await streamToBuffer(stream), extension: uploadedFile.extension }); | ||
| } catch (err: unknown) { | ||
| this.log.error({ msg: 'Failed to fetch file buffer', err }); | ||
| // Push empty buffer so parser processes this as "unsupported file" | ||
|
|
@@ -363,13 +364,11 @@ export class OmnichannelTranscript extends ServiceClass implements IOmnichannelT | |
| const transcriptText = i18n.t('Transcript'); | ||
|
|
||
| const stream = await this.worker.renderToStream({ data, i18n }); | ||
| const outBuff = await streamToBuffer(stream as Readable); | ||
|
|
||
| try { | ||
| const { rid } = await roomService.createDirectMessage({ to: details.userId, from: 'rocket.cat' }); | ||
| const [rocketCatFile, transcriptFile] = await this.uploadFiles({ | ||
| details, | ||
| buffer: outBuff, | ||
| streamParam: Readable.from(stream), | ||
| roomIds: [rid, details.rid], | ||
| data, | ||
| transcriptText, | ||
|
|
@@ -406,23 +405,20 @@ export class OmnichannelTranscript extends ServiceClass implements IOmnichannelT | |
| } | ||
|
|
||
| private async uploadFiles({ | ||
| details, | ||
| buffer, | ||
| streamParam, | ||
| roomIds, | ||
| data, | ||
| transcriptText, | ||
| }: { | ||
| details: WorkDetailsWithSource; | ||
| buffer: Buffer; | ||
| streamParam: Readable; | ||
| roomIds: string[]; | ||
| data: Pick<WorkerData, 'siteName' | 'visitor'>; | ||
| transcriptText: string; | ||
| }): Promise<IUpload[]> { | ||
| return Promise.all( | ||
| roomIds.map((roomId) => { | ||
| return uploadService.uploadFile({ | ||
| userId: details.userId, | ||
| buffer, | ||
| return uploadService.uploadFileFromStream({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: The usage of Ensure Prompt for AI agents |
||
| streamParam, | ||
| details: { | ||
| // transcript_{company-name}_{date}_{hour}.pdf | ||
| name: `${transcriptText}_${data.siteName}_${new Intl.DateTimeFormat('en-US').format(new Date()).replace(/\//g, '-')}_${ | ||
|
|
@@ -432,7 +428,6 @@ export class OmnichannelTranscript extends ServiceClass implements IOmnichannelT | |
| rid: roomId, | ||
| // Rocket.cat is the goat | ||
| userId: 'rocket.cat', | ||
| size: buffer.length, | ||
| }, | ||
| }); | ||
| }), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sharing a single
Readableacross multiple consumers will fail.This method is called from
OmnichannelTranscript.uploadFiles(line 420-432 ofOmnichannelTranscript.ts), which maps overroomIdsand callsuploadFileFromStreamfor each room using the samestreamParam. AReadablestream can only be consumed once — the first.pipe()will drain it, and subsequent calls will receive an already-ended (or empty) stream.In
OmnichannelTranscript.tslines 370-375, a singleReadable.from(stream)is created and passed touploadFiles, which then fans it out viaPromise.allto multiple rooms. The second upload will get no data.🐛 Possible approaches
Option A: Buffer the stream once, then upload from the buffer to each room:
private async uploadFiles({ - streamParam, + buffer, roomIds, ... }) { return Promise.all( roomIds.map((roomId) => { - return uploadService.uploadFileFromStream({ streamParam, details: { ... } }); + return uploadService.uploadFile({ buffer, details: { ..., size: buffer.length } }); }), ); }Option B: Clone/tee the stream for each consumer (e.g., using
stream.PassThroughor buffering once then wrapping withReadable.from()).Option C: Upload once, then copy the upload record for the second room.
🤖 Prompt for AI Agents