UNPKG

@wllama/wllama

Version:

WebAssembly binding for llama.cpp - Enabling on-browser LLM inference

116 lines (101 loc) 3.27 kB
import fs from 'fs'; import path from 'path'; const inputPath = './cpp/glue.hpp'; const outputPath = './src/glue/messages.ts'; const snakeToCamel = (str) => str.replace(/([-_]\w)/g, (g) => g[1].toUpperCase()); const snakeToPascal = (str) => { let camelCase = snakeToCamel(str); let pascalCase = camelCase[0].toUpperCase() + camelCase.substr(1); return pascalCase; }; const cppContent = fs.readFileSync(inputPath, 'utf8'); const lines = cppContent.split('\n'); const protos = []; let insideStruct = false; let currentProto = { name: '', className: '', structName: '', fields: [] }; let version = 0; for (const line of lines) { if (line.startsWith('#define GLUE_VERSION')) { version = parseInt(line.split(' ').at(-1)); } if (line.startsWith('struct glue_msg_')) { insideStruct = true; const structName = line.split(' ')[1]; currentProto = { name: '', structName, className: snakeToPascal(structName), fields: [], }; } const lineTrimed = line.trim(); if (insideStruct) { if (lineTrimed.startsWith('GLUE_HANDLER')) { const name = lineTrimed.split('(')[1].split(')')[0]; currentProto.name = JSON.parse(name); if (currentProto.name.length !== 8) { console.error( 'Invalid name (must be 8 characters):', currentProto.name ); process.exit(1); } } if (lineTrimed.startsWith('GLUE_FIELD')) { const isNullable = lineTrimed.startsWith('GLUE_FIELD_NULLABLE'); const fieldParts = lineTrimed.split('(')[1].split(')')[0].split(','); currentProto.fields.push({ type: fieldParts[0].trim(), name: fieldParts[1].trim(), isNullable, }); } if (line.startsWith('};')) { insideStruct = false; protos.push(currentProto); } } } //console.log(JSON.stringify(protos, null, 2)); const typeMapping = { bool: 'boolean', int: 'number', float: 'number', str: 'string', raw: 'Uint8Array', arr_bool: 'bool[]', arr_int: 'number[]', arr_float: 'number[]', arr_str: 'string[]', arr_raw: 'Uint8Array[]', }; const protoMap = {}; for (const proto of protos) { protoMap[proto.name] = proto; } const outputContent = `// This file is generated by cpp/generate_glue_prototype.js // Do not edit this file directly import type { GlueMessageProto } from './glue'; export const GLUE_VERSION = ${version}; export const GLUE_MESSAGE_PROTOTYPES: { [name: string]: GlueMessageProto } = ${JSON.stringify(protoMap, null, 2)}; ${protos .map((proto) => { let interfaceCode = `// struct ${proto.structName}\n`; interfaceCode += `export interface ${proto.className} {\n`; interfaceCode += ` _name: "${proto.name}";\n`; for (const field of proto.fields) { const mappedType = typeMapping[field.type]; if (!mappedType) { console.error('Unknown type:', field.type); process.exit(1); } interfaceCode += ` ${field.name}${field.isNullable ? '?' : ''}: ${mappedType}${field.isNullable ? ' | undefined' : ''};\n`; } interfaceCode += '}\n'; return interfaceCode; }) .join('\n')} export type GlueMsg = ${protos.map((proto) => proto.className).join(' | ')}; `; fs.writeFileSync(outputPath, outputContent);