Instructions to use dizyann/crivo-web with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers.js
How to use dizyann/crivo-web with Transformers.js:
// ⚠️ Unknown pipeline tag
Download scripts/conversion/export.py from dizyann/crivo-web: direct link, hf CLI and curl.
- Browser
- Download file 6.9 kB
-
https://huggingface.co/dizyann/crivo-web/resolve/main/scripts/conversion/export.py
- Command line
-
hf download hf://dizyann/crivo-web/scripts/conversion/export.py
-
curl -L -o export.py https://huggingface.co/dizyann/crivo-web/resolve/main/scripts/conversion/export.py
6.9 kB
| """Reproducible full-graph Crivo export. Conversion-only Python; app runtime stays TypeScript. | |
| Run with the pinned environment in requirements.txt. Source snapshots must match sources.json. | |
| """ | |
| import argparse, gc, importlib.util, json, shutil, sys | |
| from pathlib import Path | |
| import numpy as np | |
| import onnx | |
| import onnxruntime as ort | |
| from quantize import quantize_weights | |
| import torch | |
| from transformers import AutoTokenizer, ModernBertConfig, ModernBertModel | |
| from safetensors.torch import load_file | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('size', choices=['17m','32m','68m','150m','400m','1b']) | |
| args = parser.parse_args() | |
| root = Path(__file__).resolve().parents[2] | |
| source = root / '.cache/conversion' / args.size | |
| out = root / '.cache/variants' / args.size | |
| out.mkdir(parents=True, exist_ok=True) | |
| (out/'onnx').mkdir(exist_ok=True) | |
| meta = json.loads((source/'source.json').read_text()) | |
| assert meta == json.loads(Path(__file__).with_name('sources.json').read_text())[args.size], 'Source revision mismatch' | |
| torch.set_num_threads(4) | |
| spec = importlib.util.spec_from_file_location('upstream_model', source/'model.py') | |
| upstream = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(upstream) | |
| original = json.loads((source/'config.json').read_text()) | |
| config = ModernBertConfig(**{k:v for k,v in original.items() if k not in ('architectures','decision')}) | |
| config._attn_implementation = 'eager' | |
| config.reference_compile = False | |
| model = upstream.DecisionModel(ModernBertModel(config), config.hidden_size).eval() | |
| model.load_state_dict(load_file(str(source/'model.safetensors')), strict=True) | |
| tok = AutoTokenizer.from_pretrained(source) | |
| tok.save_pretrained(out) | |
| tc = json.loads((out/'tokenizer_config.json').read_text()) | |
| tc['tokenizer_class'] = 'PreTrainedTokenizer' | |
| (out/'tokenizer_config.json').write_text(json.dumps(tc,indent=2)+'\n') | |
| exported = {**original, 'model_type':'custom','architectures':['PreTrainedModel'], 'source':meta} | |
| # Browser release initially retains the tested 7,999-token cap, not upstream's 32k claim. | |
| exported['web_max_length'] = 7999 | |
| names = ['input_ids','attention_mask','opt_positions','opt_mask','qtype'] | |
| encoder = upstream.Encoder(tok) | |
| cases = [ | |
| ('Please refund my duplicate payment.', [('choice','Identify the intent.',{'refund':'Return money','cancel':'Cancel order','other':'Another request'}),('noul','The customer was charged twice.',None),('score','Rate urgency.',['Low','Medium','High'])]), | |
| ('The order is fine, I only want to know the delivery date.', [('choice','Identify intent.',{'question':'Ask a question','refund':'Return money'} )]), | |
| ('A long support request. '*30,[('score','Rate urgency.',['Low','High']),('noul','The service is unavailable.',{'true':'Unavailable','false':'Available'})]) | |
| ] | |
| def inputs(state, questions): | |
| rows=[] | |
| for kind,instructions,criteria in questions: | |
| options,_=upstream.option_texts(kind,criteria,instructions) | |
| ids,pos=encoder.encode(instructions,state,options) | |
| rows.append((ids,pos,upstream.QUESTION_TYPES[kind])) | |
| return upstream.collate(rows,tok.pad_token_id) | |
| samples=[inputs(*case) for case in cases] | |
| references=[] | |
| with torch.inference_mode(): | |
| for feeds in samples: references.append(model(**feeds).numpy()) | |
| graph=out/'onnx/model.onnx' | |
| print('EXPORT',args.size,flush=True) | |
| with torch.no_grad(): | |
| torch.onnx.export(model,tuple(samples[0][name] for name in names),str(graph),input_names=names,output_names=['logits'],opset_version=17,dynamo=False,external_data=True,dynamic_axes={**{n:{0:'batch',1:'sequence'} for n in names[:2]},**{n:{0:'batch',1:'options'} for n in names[2:4]},'qtype':{0:'batch'},'logits':{0:'batch',1:'options'}}) | |
| del model;gc.collect() | |
| # Keep each external buffer below browser ArrayBuffer limits. | |
| def shard_external(path): | |
| g=onnx.load(str(path)) | |
| index=0;offset=0;handle=None | |
| try: | |
| for tensor in g.graph.initializer: | |
| if not tensor.HasField('raw_data'): continue | |
| data=tensor.raw_data | |
| if handle is None or offset+len(data)>1024**3: | |
| if handle: handle.close();index+=1 | |
| location=path.name+'_data'+('' if index==0 else '_'+str(index)) | |
| handle=(path.parent/location).open('wb');offset=0 | |
| onnx.external_data_helper.set_external_data(tensor,location,offset,len(data)) | |
| handle.write(data);offset+=len(data) | |
| tensor.ClearField('raw_data');tensor.data_location=onnx.TensorProto.EXTERNAL | |
| finally: | |
| if handle: handle.close() | |
| onnx.save_model(g,str(path)) | |
| del g;gc.collect() | |
| return index+1 | |
| if args.size=='1b': | |
| exported['transformers.js_config']={'use_external_data_format':{'model.onnx':shard_external(graph)}} | |
| (out/'config.json').write_text(json.dumps(exported,indent=2)+'\n') | |
| onnx.checker.check_model(str(graph)) | |
| opts=ort.SessionOptions();opts.intra_op_num_threads=4 | |
| session=ort.InferenceSession(str(graph),sess_options=opts,providers=['CPUExecutionProvider']) | |
| fp_delta=0 | |
| for feeds,expected in zip(samples,references): | |
| actual=session.run(['logits'],{n:feeds[n].numpy() for n in names})[0] | |
| np.testing.assert_allclose(actual,expected,rtol=1e-3,atol=1e-4) | |
| fp_delta=max(fp_delta,float(np.max(np.abs(actual-expected)))) | |
| del session;gc.collect() | |
| print('QUANTIZE',args.size,flush=True) | |
| quant=out/'onnx/model_quantized.onnx' | |
| quantize_weights(graph, quant) | |
| if args.size=='1b': | |
| exported['transformers.js_config']['use_external_data_format']['model_quantized.onnx']=shard_external(quant) | |
| (out/'config.json').write_text(json.dumps(exported,indent=2)+'\n') | |
| onnx.checker.check_model(str(quant)) | |
| session=ort.InferenceSession(str(quant),sess_options=opts,providers=['CPUExecutionProvider']) | |
| quant_delta=0 | |
| fixtures=[] | |
| for (state,questions),feeds,expected in zip(cases,samples,references): | |
| actual=session.run(['logits'],{n:feeds[n].numpy() for n in names})[0] | |
| assert np.isfinite(actual).all() | |
| p=torch.from_numpy(expected).double().softmax(-1).numpy() | |
| q=torch.from_numpy(actual).double().softmax(-1).numpy() | |
| quant_delta=max(quant_delta,float(np.max(np.abs(p-q)))) | |
| fixtures.append({'state':state,'questions':[{'type':k,'instructions':i,**({'criteria':c} if c is not None else {})} for k,i,c in questions],'fp32':p.tolist(),'int8':q.tolist()}) | |
| report={'quantization':{'storage':'int8','arithmetic':'fp32','scheme':'symmetric-per-channel-weight-only'},'source':meta,'fp32_max_logit_error':fp_delta,'int8_max_probability_error':quant_delta,'cases':fixtures,'versions':{'torch':torch.__version__,'onnx':onnx.__version__,'onnxruntime':ort.__version__}} | |
| # Retain only the final graphs and referenced external buffers. | |
| keep = {'model.onnx', 'model_quantized.onnx'} | |
| for path in [graph, quant]: | |
| g=onnx.load(str(path),load_external_data=False) | |
| for tensor in g.graph.initializer: | |
| keep.update(item.value for item in tensor.external_data if item.key=='location') | |
| for path in (out/'onnx').iterdir(): | |
| if path.is_file() and path.name not in keep: path.unlink() | |
| (out/'parity.json').write_text(json.dumps(report,indent=2)+'\n') | |
| print('PASS',args.size,fp_delta,quant_delta,flush=True) | |