Spaces:
Build error
Build error
File size: 3,488 Bytes
b3fb4c5 66db317 b3fb4c5 3b1978f b3fb4c5 62dc75f b3fb4c5 66db317 b3fb4c5 3b1978f b3fb4c5 51a4877 b3fb4c5 51a4877 b3fb4c5 8a2b095 b3fb4c5 62dc75f 8a2b095 b3fb4c5 8a2b095 b3fb4c5 8a2b095 b3fb4c5 8a2b095 b3fb4c5 3b1978f 8a2b095 b3fb4c5 8a2b095 b3fb4c5 8a2b095 b3fb4c5 | 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 | import { AssertionFailureError, AsyncService, HashManager } from 'civkit';
import { singleton } from 'tsyringe';
import { GlobalLogger } from './logger';
import { CanvasService } from '../shared/services/canvas';
import { ImageInterrogationManager } from '../shared/services/common-iminterrogate';
import { ImgBrief } from './puppeteer';
import { ImgAlt } from '../db/img-alt';
import { AsyncLocalContext } from './async-context';
const md5Hasher = new HashManager('md5', 'hex');
@singleton()
export class AltTextService extends AsyncService {
altsToIgnore = 'image,img,photo,picture,pic,alt,figure,fig'.split(',');
logger = this.globalLogger.child({ service: this.constructor.name });
constructor(
protected globalLogger: GlobalLogger,
protected imageInterrogator: ImageInterrogationManager,
protected canvasService: CanvasService,
protected asyncLocalContext: AsyncLocalContext
) {
super(...arguments);
}
override async init() {
await this.dependencyReady();
this.emit('ready');
}
async caption(url: string) {
try {
const img = await this.canvasService.loadImage(url);
const resized = this.canvasService.fitImageToSquareBox(img, 1024);
const exported = await this.canvasService.canvasToBuffer(resized, 'image/png');
const r = await this.imageInterrogator.interrogate('vertex-gemini-1.5-flash-002', {
image: exported,
prompt: `Yield a concise image caption sentence in third person.`,
system: 'You are BLIP2, an image caption model.',
});
return r.replaceAll(/[\n\"]|(\.\s*$)/g, '').trim();
} catch (err) {
throw new AssertionFailureError({ message: `Could not generate alt text for url ${url}`, cause: err });
}
}
async getAltText(imgBrief: ImgBrief) {
if (!imgBrief.src) {
return undefined;
}
if (imgBrief.alt && !this.altsToIgnore.includes(imgBrief.alt.trim().toLowerCase())) {
return imgBrief.alt;
}
const digest = md5Hasher.hash(imgBrief.src);
const shortDigest = Buffer.from(digest, 'hex').toString('base64url');
const existing = await ImgAlt.fromFirestore(shortDigest);
if (existing) {
return existing.generatedAlt || existing.originalAlt || '';
}
let generatedCaption = '';
try {
generatedCaption = await this.caption(imgBrief.src);
} catch (err) {
this.logger.warn(`Unable to generate alt text for ${imgBrief.src}`, { err });
}
if (this.asyncLocalContext.ctx.DNT) {
// Don't cache alt text if DNT is set
return;
}
// Don't try again until the next day
const expireMixin = generatedCaption ? {} : { expireAt: new Date(Date.now() + 1000 * 3600 * 24) };
await ImgAlt.COLLECTION.doc(shortDigest).set(
{
_id: shortDigest,
src: imgBrief.src || '',
width: imgBrief.naturalWidth || 0,
height: imgBrief.naturalHeight || 0,
urlDigest: digest,
originalAlt: imgBrief.alt || '',
generatedAlt: generatedCaption || '',
createdAt: new Date(),
...expireMixin
}, { merge: true }
);
return generatedCaption;
}
}
|