7254f7b4d1
Build / Build (macos-latest) (push) Has been cancelled
Build / Build (ubuntu-latest) (push) Has been cancelled
Build / Build (windows-latest) (push) Has been cancelled
Build / Analyze (javascript) (push) Has been cancelled
Build / Analyze (python) (push) Has been cancelled
144 lines
5.7 KiB
JavaScript
144 lines
5.7 KiB
JavaScript
|
|
import * as flatbuffers from './flatbuffers.js';
|
|
import * as protobuf from './protobuf.js';
|
|
|
|
const litertlm = {};
|
|
|
|
litertlm.ModelFactory = class {
|
|
|
|
async match(context) {
|
|
const stream = context.stream;
|
|
if (stream && stream.length >= 32) {
|
|
const buffer = stream.peek(8);
|
|
const magic = String.fromCharCode(...buffer);
|
|
if (magic === 'LITERTLM') {
|
|
return context.set('litertlm', stream);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async open(context) {
|
|
litertlm.schema = await context.require('./litertlm-schema');
|
|
litertlm.schema = litertlm.schema.litert.lm.schema;
|
|
const stream = context.value;
|
|
const header = stream.peek(32);
|
|
const view = new DataView(header.buffer, header.byteOffset, header.byteLength);
|
|
const major = view.getUint32(8, true);
|
|
const minor = view.getUint32(12, true);
|
|
const patch = view.getUint32(16, true);
|
|
const version = `${major}.${minor}.${patch}`;
|
|
const reader = flatbuffers.BinaryReader.open(stream, 32);
|
|
if (!reader) {
|
|
throw new litertlm.Error('Invalid LiteRT-LM header.');
|
|
}
|
|
const data = litertlm.schema.LiteRTLMMetaData.create(reader);
|
|
const sections = data.section_metadata ? data.section_metadata.objects : [];
|
|
const AnySectionDataType = litertlm.schema.AnySectionDataType;
|
|
const modules = [];
|
|
let metadata = null;
|
|
for (const section of sections) {
|
|
const begin = section.begin_offset.toNumber();
|
|
const end = section.end_offset.toNumber();
|
|
switch (section.data_type) {
|
|
case AnySectionDataType.TFLiteModel: {
|
|
stream.seek(begin);
|
|
const buffer = stream.read(end - begin);
|
|
const content = context.context(`model_${modules.length}.tflite`, buffer);
|
|
const tflite = await context.require('./tflite'); // eslint-disable-line no-await-in-loop
|
|
const modelFactory = new tflite.ModelFactory();
|
|
await modelFactory.match(content); // eslint-disable-line no-await-in-loop
|
|
const model = await modelFactory.open(content); // eslint-disable-line no-await-in-loop
|
|
if (model && Array.isArray(model.modules)) {
|
|
for (const module of model.modules) {
|
|
modules.push(module);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case AnySectionDataType.SP_Tokenizer: {
|
|
stream.seek(begin);
|
|
const buffer = stream.read(end - begin);
|
|
const content = context.context('tokenizer.model', buffer);
|
|
content.set('sentencepiece');
|
|
const sentencepiece = await context.require('./sentencepiece'); // eslint-disable-line no-await-in-loop
|
|
const modelFactory = new sentencepiece.ModelFactory();
|
|
const model = await modelFactory.open(content); // eslint-disable-line no-await-in-loop
|
|
if (model && Array.isArray(model.modules)) {
|
|
for (const module of model.modules) {
|
|
modules.push(module);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case AnySectionDataType.LlmMetadataProto: {
|
|
litertlm.proto = await context.require('./litertlm-proto'); // eslint-disable-line no-await-in-loop
|
|
litertlm.proto = litertlm.proto.litert.lm.proto;
|
|
stream.seek(begin);
|
|
const buffer = stream.read(end - begin);
|
|
const pbReader = protobuf.BinaryReader.open(buffer);
|
|
metadata = litertlm.proto.LlmMetadata.decode(pbReader);
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return new litertlm.Model(version, data, sections, modules, metadata);
|
|
}
|
|
};
|
|
|
|
litertlm.Model = class {
|
|
|
|
constructor(version, data, sections, modules, metadata) {
|
|
this.format = `LiteRT-LM v${version}`;
|
|
this.modules = modules;
|
|
this.metadata = [];
|
|
if (data.system_metadata) {
|
|
for (const entry of data.system_metadata.entries) {
|
|
if (entry.value) {
|
|
this.metadata.push(new litertlm.Argument(entry.key, entry.value.value));
|
|
}
|
|
}
|
|
}
|
|
if (metadata) {
|
|
const format = (value) => {
|
|
if (value === null || value === undefined || value === 0 || value === '') {
|
|
return null;
|
|
}
|
|
if (Array.isArray(value) || typeof value === 'object') {
|
|
const values = Array.isArray(value) ? value : Object.values(value);
|
|
const items = values.map((v) => format(v)).filter((v) => v !== null);
|
|
return items.length > 0 ? items.join(', ') : null;
|
|
}
|
|
return value;
|
|
};
|
|
for (const [name, value] of Object.entries(metadata)) {
|
|
const formatted = format(value);
|
|
if (formatted !== null) {
|
|
this.metadata.push(new litertlm.Argument(name, formatted));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
litertlm.Argument = class {
|
|
|
|
constructor(name, value) {
|
|
this.name = name;
|
|
this.value = value;
|
|
}
|
|
};
|
|
|
|
litertlm.Error = class extends Error {
|
|
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'Error loading LiteRT-LM model.';
|
|
}
|
|
};
|
|
|
|
export const ModelFactory = litertlm.ModelFactory;
|