Files
wehub-resource-sync 7254f7b4d1
Build / Build (macos-latest) (push) Waiting to run
Build / Build (ubuntu-latest) (push) Waiting to run
Build / Build (windows-latest) (push) Waiting to run
Build / Analyze (javascript) (push) Waiting to run
Build / Analyze (python) (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 12:37:45 +08:00

367 lines
13 KiB
JavaScript

import * as fs from 'fs/promises';
import * as path from 'path';
import * as protobuf from '../source/protobuf.js';
import * as url from 'url';
import { tensorflow } from '../source/tf-proto.js';
const decoder = new TextDecoder('utf-8');
const has = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
const dataTypes = new Map([
[tensorflow.DataType.DT_HALF, 'float16'],
[tensorflow.DataType.DT_FLOAT, 'float32'],
[tensorflow.DataType.DT_DOUBLE, 'float64'],
[tensorflow.DataType.DT_INT32, 'int32'],
[tensorflow.DataType.DT_UINT8, 'uint8'],
[tensorflow.DataType.DT_UINT16, 'uint16'],
[tensorflow.DataType.DT_UINT32, 'uint32'],
[tensorflow.DataType.DT_UINT64, 'uint64'],
[tensorflow.DataType.DT_INT16, 'int16'],
[tensorflow.DataType.DT_INT8, 'int8'],
[tensorflow.DataType.DT_STRING, 'string'],
[tensorflow.DataType.DT_COMPLEX64, 'complex64'],
[tensorflow.DataType.DT_COMPLEX128, 'complex128'],
[tensorflow.DataType.DT_INT64, 'int64'],
[tensorflow.DataType.DT_BOOL, 'bool'],
[tensorflow.DataType.DT_QINT8, 'qint8'],
[tensorflow.DataType.DT_QUINT8, 'quint8'],
[tensorflow.DataType.DT_QINT16, 'qint16'],
[tensorflow.DataType.DT_QUINT16, 'quint16'],
[tensorflow.DataType.DT_QINT32, 'qint32'],
[tensorflow.DataType.DT_BFLOAT16, 'bfloat16'],
[tensorflow.DataType.DT_RESOURCE, 'resource'],
[tensorflow.DataType.DT_VARIANT, 'variant'],
[tensorflow.DataType.DT_HALF_REF, 'float16_ref'],
[tensorflow.DataType.DT_FLOAT_REF, 'float32_ref'],
[tensorflow.DataType.DT_DOUBLE_REF, 'float64_ref'],
[tensorflow.DataType.DT_INT32_REF, 'int32_ref'],
[tensorflow.DataType.DT_UINT32_REF, 'uint32_ref'],
[tensorflow.DataType.DT_UINT8_REF, 'uint8_ref'],
[tensorflow.DataType.DT_UINT16_REF, 'uint16_ref'],
[tensorflow.DataType.DT_INT16_REF, 'int16_ref'],
[tensorflow.DataType.DT_INT8_REF, 'int8_ref'],
[tensorflow.DataType.DT_STRING_REF, 'string_ref'],
[tensorflow.DataType.DT_COMPLEX64_REF, 'complex64_ref'],
[tensorflow.DataType.DT_COMPLEX128_REF, 'complex128_ref'],
[tensorflow.DataType.DT_INT64_REF, 'int64_ref'],
[tensorflow.DataType.DT_UINT64_REF, 'uint64_ref'],
[tensorflow.DataType.DT_BOOL_REF, 'bool_ref'],
[tensorflow.DataType.DT_QINT8_REF, 'qint8_ref'],
[tensorflow.DataType.DT_QUINT8_REF, 'quint8_ref'],
[tensorflow.DataType.DT_QINT16_REF, 'qint16_ref'],
[tensorflow.DataType.DT_QUINT16_REF, 'quint16_ref'],
[tensorflow.DataType.DT_QINT32_REF, 'qint32_ref'],
[tensorflow.DataType.DT_BFLOAT16_REF, 'bfloat16_ref'],
[tensorflow.DataType.DT_RESOURCE_REF, 'resource_ref'],
[tensorflow.DataType.DT_VARIANT_REF, 'variant_ref']
]);
const attributeTypes = new Map([
['type', 'type'], ['list(type)', 'type[]'],
['bool', 'boolean'],
['int', 'int64'], ['list(int)', 'int64[]'],
['float', 'float32'], ['list(float)', 'float32[]'],
['string', 'string'], ['list(string)', 'string[]'],
['shape', 'shape'], ['list(shape)', 'shape[]'],
['tensor', 'tensor'],
['func', 'function'], ['list(func)', 'function[]']
]);
const categories = new Map([
['Assign', 'Control'],
['AvgPool', 'Pool'],
['BatchNormWithGlobalNormalization', 'Normalization'],
['BiasAdd', 'Layer'],
['Concat', 'Tensor'],
['ConcatV2', 'Tensor'],
['Const', 'Constant'],
['Conv2D', 'Layer'],
['DepthwiseConv2dNative', 'Layer'],
['Dequantize', 'Quantization'],
['Elu', 'Activation'],
['FusedBatchNorm', 'Normalization'],
['FusedBatchNormV2', 'Normalization'],
['FusedBatchNormV3', 'Normalization'],
['Gather', 'Transform'],
['Identity', 'Control'],
['LeakyRelu', 'Activation'],
['LRN', 'Normalization'],
['LSTMBlockCell', 'Layer'],
['MaxPool', 'Pool'],
['MaxPoolV2', 'Pool'],
['MaxPoolWithArgmax', 'Pool'],
['Pad', 'Tensor'],
['QuantizeAndDequantize', 'Quantization'],
['QuantizeAndDequantizeV2', 'Quantization'],
['QuantizeAndDequantizeV3', 'Quantization'],
['QuantizeAndDequantizeV4', 'Quantization'],
['QuantizeAndDequantizeV4Grad', 'Quantization'],
['QuantizeDownAndShrinkRange', 'Quantization'],
['QuantizeV2', 'Quantization'],
['Relu', 'Activation'],
['Relu6', 'Activation'],
['Reshape', 'Shape'],
['Sigmoid', 'Activation'],
['Slice', 'Tensor'],
['Softmax', 'Activation'],
['Split', 'Tensor'],
['Squeeze', 'Transform'],
['StridedSlice', 'Tensor'],
['swish_f32', 'Activation'],
['Transpose', 'Transform'],
['Variable', 'Control'],
['VariableV2', 'Control']
]);
const convertFloat = (value) => {
if (value === Infinity) {
return 'NaN';
}
if (value === -Infinity) {
return '-NaN';
}
return Math.fround(value);
};
const escapeString = (text) => {
let result = '';
for (const c of text) {
switch (c) {
case '\n': result += '\\n'; break;
case '\r': result += '\\r'; break;
case '\t': result += '\\t'; break;
case '"': result += '\\"'; break;
case "'": result += "\\'"; break;
case '\\': result += '\\\\'; break;
default: result += c; break;
}
}
return result;
};
const heredoc = (input) => {
const lines = input.split('\n');
const output = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const colon = line.indexOf(':');
const marker = colon === -1 ? null : line.substring(colon + 1).replace(/^ +/, '');
if (marker === null || !marker.startsWith('<<')) {
output.push(line);
continue;
}
const terminator = marker.substring(2);
const content = [];
let trailing = '';
while (++i < lines.length) {
const inner = lines[i];
if (inner.startsWith(terminator)) {
trailing = inner.substring(terminator.length);
break;
}
content.push(inner);
}
output.push(`${line.substring(0, colon + 1)}"${escapeString(content.join('\n'))}"${trailing}`);
}
return output.join('\n');
};
const readOpList = async (file) => {
const content = await fs.readFile(file, 'utf-8');
const encoder = new TextEncoder();
const reader = new protobuf.TextReader(encoder.encode(content));
return tensorflow.OpList.decodeText(reader);
};
const readApiDefs = async (folder) => {
const dirs = await fs.readdir(folder);
const files = dirs.filter((name) => name.endsWith('.pbtxt'));
const encoder = new TextEncoder();
const defs = new Map();
for (const name of files.sort()) {
const file = path.join(folder, name);
// eslint-disable-next-line no-await-in-loop
const content = await fs.readFile(file, 'utf-8');
const text = heredoc(content);
const buffer = encoder.encode(text);
const reader = protobuf.TextReader.open(buffer);
const apiDefs = tensorflow.ApiDefs.decodeText(reader);
for (const op of apiDefs.op) {
defs.set(op.graph_op_name, op);
}
}
return defs;
};
const convertAttrList = (attrValue) => {
const result = [];
const list = attrValue.list;
for (const value of list.s) {
result.push(decoder.decode(value));
}
for (const value of list.i) {
result.push(typeof value === 'bigint' ? Number(value) : value);
}
for (const value of list.f) {
result.push(convertFloat(value));
}
for (const value of list.type) {
result.push({ type: 'type', value });
}
if (result.length === 0 && (list.b.length > 0 || list.shape.length > 0 ||
list.tensor.length > 0 || list.func.length > 0)) {
throw new Error('Unsupported list value.');
}
return result;
};
const convertAttrValue = (attrValue) => {
if (has(attrValue, 'list')) {
return convertAttrList(attrValue);
}
if (has(attrValue, 's')) {
return decoder.decode(attrValue.s);
}
if (has(attrValue, 'i')) {
return typeof attrValue.i === 'bigint' ? Number(attrValue.i) : attrValue.i;
}
if (has(attrValue, 'f')) {
return convertFloat(attrValue.f);
}
if (has(attrValue, 'b')) {
return attrValue.b;
}
if (has(attrValue, 'type')) {
return { type: 'type', value: attrValue.type };
}
if (has(attrValue, 'tensor')) {
return { type: 'tensor', value: '?' };
}
if (has(attrValue, 'shape')) {
return { type: 'shape', value: '?' };
}
throw new Error('Unsupported attribute value.');
};
const formatAttributeValue = (value) => {
if (value && typeof value === 'object' && value.type === 'type') {
if (!dataTypes.has(value.value)) {
throw new Error(`Unknown data type '${value.value}'.`);
}
return dataTypes.get(value.value);
}
if (typeof value === 'string') {
return value;
}
if (value === true || value === false) {
return value.toString();
}
throw new Error('Unsupported attribute value.');
};
const buildSchema = (operator, apiDef) => {
const schema = { name: operator.name };
if (categories.has(operator.name)) {
schema.category = categories.get(operator.name);
}
if (apiDef.summary) {
schema.summary = apiDef.summary;
}
if (apiDef.description) {
schema.description = apiDef.description;
}
const apiDefAttr = new Map(apiDef.attr.map((attr) => [attr.name, attr]));
for (const attr of operator.attr) {
if (!attributeTypes.has(attr.type)) {
throw new Error(`Unknown attribute type '${attr.type}'.`);
}
const json = { name: attr.name, type: attributeTypes.get(attr.type) };
const description = apiDefAttr.get(attr.name)?.description;
if (description) {
json.description = description;
}
if (attr.has_minimum) {
json.minimum = Number(attr.minimum);
}
if (attr.allowed_values !== null) {
const allowed = convertAttrValue(attr.allowed_values).map((v) => `\`${formatAttributeValue(v)}\``).join(', ');
const prefix = json.description ? `${json.description} ` : '';
json.description = `${prefix}Must be one of the following: ${allowed}.`;
}
if (attr.default_value !== null) {
json.default = convertAttrValue(attr.default_value);
}
schema.attributes = schema.attributes || [];
schema.attributes.push(json);
}
const apiDefIn = new Map(apiDef.in_arg.map((arg) => [arg.name, arg]));
for (const arg of operator.input_arg) {
const json = { name: arg.name };
const description = apiDefIn.get(arg.name)?.description;
if (description) {
json.description = description;
}
if (arg.number_attr) {
json.numberAttr = arg.number_attr;
}
if (arg.type) {
json.type = arg.type;
}
if (arg.type_attr) {
json.typeAttr = arg.type_attr;
}
if (arg.type_list_attr) {
json.typeListAttr = arg.type_list_attr;
}
if (arg.is_ref) {
json.isRef = true;
}
schema.inputs = schema.inputs || [];
schema.inputs.push(json);
}
const apiDefOut = new Map(apiDef.out_arg.map((arg) => [arg.name, arg]));
for (const arg of operator.output_arg) {
const json = { name: arg.name };
const description = apiDefOut.get(arg.name)?.description;
if (description) {
json.description = description;
}
if (arg.number_attr) {
json.numberAttr = arg.number_attr;
}
if (arg.type) {
json.type = arg.type;
} else if (arg.type_attr) {
json.typeAttr = arg.type_attr;
} else if (arg.type_list_attr) {
json.typeListAttr = arg.type_list_attr;
}
if (arg.is_ref) {
json.isRef = true;
}
schema.outputs = schema.outputs || [];
schema.outputs.push(json);
}
return schema;
};
const main = async () => {
const root = path.dirname(path.dirname(url.fileURLToPath(import.meta.url)));
const dir = path.join(root, 'third_party', 'source', 'tensorflow', 'tensorflow', 'core');
const defs = await readApiDefs(path.join(dir, 'api_def', 'base_api'));
const ops = await readOpList(path.join(dir, 'ops', 'ops.pbtxt'));
const schemas = [];
for (const op of ops.op) {
const apiDef = defs.get(op.name) || new tensorflow.ApiDef();
const schema = buildSchema(op, apiDef);
schemas.push(schema);
}
const file = path.join(root, 'source', 'tf-metadata.json');
const content = JSON.stringify(schemas, null, 2);
await fs.writeFile(file, content, 'utf-8');
};
await main();