Sariel00 commited on
Commit
022e6e4
·
verified ·
1 Parent(s): 3fd1a35

Publish Ling-3.0-tiny RKNN engine and model (part 2)

Browse files

Includes the final RK3588 model, verified portable runtime, canonical C++ source, conversion tooling, evaluation docs, and third-party notices.

tools/validate_selective_release_board.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Isolated portable release acceptance; restore the existing 64K service on exit."""
3
+ import argparse
4
+ import hashlib
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ import signal
9
+ import socket
10
+ import statistics
11
+ import subprocess
12
+ import tempfile
13
+ import time
14
+ import urllib.request
15
+
16
+
17
+ def main():
18
+ p=argparse.ArgumentParser(description=__doc__)
19
+ p.add_argument('--root',type=Path,required=True)
20
+ a=p.parse_args()
21
+ root=a.root.resolve()
22
+ package=root/'package/mindnano-ling3-rk3588-selective-20260911'
23
+ output=root/'validation'
24
+ output.mkdir(exist_ok=False)
25
+ deployment=root/'deployment/mindnano-ling3-rk3588'
26
+ reference=root/'calibrated-selective-20260911/kda_out_mla_in_mla_out_shared_dense_head'
27
+ suite=root/'mixed-precision-reference/suite.json'
28
+ env={'PATH':'/usr/bin:/bin','LANG':'C.UTF-8','no_proxy':'*'}
29
+ client=urllib.request.build_opener(urllib.request.ProxyHandler({}))
30
+ result={}
31
+ def request(port,path,body=None):
32
+ r=urllib.request.Request(f'http://127.0.0.1:{port}'+path,
33
+ data=None if body is None else json.dumps(body).encode(),headers={'Content-Type':'application/json'})
34
+ with client.open(r,timeout=600) as f:return json.load(f)
35
+ def ready(port,child=None):
36
+ deadline=time.monotonic()+600
37
+ while time.monotonic()<deadline:
38
+ if child is not None and child.poll() is not None:raise RuntimeError('service exited: '+str(child.returncode))
39
+ try:
40
+ h=request(port,'/health')
41
+ if h['status']=='ok':return h
42
+ except (OSError,ValueError):pass
43
+ time.sleep(1)
44
+ raise TimeoutError('service initialization')
45
+ def stop(child):
46
+ if child.poll() is None:
47
+ child.terminate()
48
+ try:child.wait(timeout=30)
49
+ except subprocess.TimeoutExpired:
50
+ os.killpg(child.pid,signal.SIGKILL);child.wait()
51
+ raise RuntimeError('test process required SIGKILL')
52
+ if child.returncode:raise RuntimeError('test process exit: '+str(child.returncode))
53
+ def interrupt(signum,frame):raise SystemExit(128+signum)
54
+ for s in (signal.SIGINT,signal.SIGTERM):signal.signal(s,interrupt)
55
+ result['health_before']=request(9091,'/health')
56
+ assert not result['health_before']['active'] and result['health_before']['context_length']==65536
57
+ with socket.socket() as s:
58
+ s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1);s.bind(('127.0.0.1',19096))
59
+ (output/'health-before.json').write_text(json.dumps(result['health_before'],indent=2)+'\n')
60
+ try:
61
+ subprocess.run(['systemctl','--user','stop','mindnano-ling3.service'],check=True)
62
+ print('PHASE portable package 64K service, clean environment and unrelated cwd',flush=True)
63
+ with tempfile.TemporaryDirectory(prefix='ling3-release-empty-') as cwd, (output/'service.log').open('w') as log:
64
+ child=subprocess.Popen([str(package/'mindnano-infer'),'--context','64K','--no-console',
65
+ '--host','127.0.0.1','--port','19096','--log',str(output/'metrics.jsonl')],
66
+ cwd=cwd,env=env,stdout=log,stderr=subprocess.STDOUT,start_new_session=True)
67
+ try:
68
+ h=ready(19096,child);result['health']=h
69
+ assert h['context_length']==65536
70
+ fmt=h['weight_format']
71
+ assert fmt['source']=='self_contained_selective_w4_w8' and fmt['shared_stage']
72
+ assert fmt['linear_counts']=={'original_w4':5888,'calibrated_w4':18,'w8':91}
73
+ body={'model':'mindnano-ling3-tiny','temperature':0,'max_tokens':64,'cache_prompt':False,
74
+ 'messages':[{'role':'user','content':'1'*107}]}
75
+ result['warm_request']=request(19096,'/v1/chat/completions',body)
76
+ result['runs']=[]
77
+ for i in range(3):
78
+ r=request(19096,'/v1/chat/completions',body)
79
+ assert r['usage']['prompt_tokens']==128 and r['usage']['completion_tokens']==64
80
+ assert r['mindnano_metrics']['cached_tokens']==0
81
+ result['runs'].append(r)
82
+ print(json.dumps(dict(run=i+1,metrics=r['mindnano_metrics'])),flush=True)
83
+ result['performance']={
84
+ 'ttft_median_ms':statistics.median(r['mindnano_metrics']['ttft_ms'] for r in result['runs']),
85
+ 'decode_median_tokens_per_second':statistics.median(r['mindnano_metrics']['decode_tokens_per_second'] for r in result['runs']),
86
+ 'peak_rss_mib':max(r['mindnano_metrics']['peak_rss_mib'] for r in result['runs'])}
87
+ with (output/'api.json').open('w') as f:
88
+ subprocess.run(['/usr/bin/python3',str(root/'source/tests/portable_api_test.py'),
89
+ '--url','http://127.0.0.1:19096'],env=env,stdout=f,check=True)
90
+ print('PASS portable API / SSE / cancellation / request isolation',flush=True)
91
+ with (output/'prefix-cache.log').open('w') as f:
92
+ subprocess.run(['/usr/bin/python3',str(root/'source/tests/prefix_cache_api_test.py'),
93
+ '--url','http://127.0.0.1:19096','--output',str(output/'prefix-cache.json')],env=env,stdout=f,check=True)
94
+ print('PASS prefix cache / branching / namespace / streaming',flush=True)
95
+ finally:stop(child)
96
+ print('PHASE full Think / No-think equality against tested candidate',flush=True)
97
+ logits=output/'logits'
98
+ with (output/'probe.log').open('w') as log:
99
+ child=subprocess.Popen([str(root/'build/ling3-quant-loss-probe'),str(package/'ling3-tiny-w4.l3r'),
100
+ str(suite),str(logits)],env=env,stdout=log,stderr=subprocess.STDOUT,start_new_session=True)
101
+ try:
102
+ if child.wait()!=0:raise RuntimeError('full numerical probe failed')
103
+ finally:stop(child)
104
+ result['logit_equality']=[]
105
+ for case in json.loads(suite.read_text())['cases']:
106
+ name=case['id']+'.rknn.f32'
107
+ hashes=[]
108
+ for directory in (reference,logits):
109
+ file=directory/name
110
+ assert file.stat().st_size==len(case['teacher_ids'])*157184*4
111
+ with file.open('rb') as f:hashes.append(hashlib.file_digest(f,'sha256').hexdigest())
112
+ result['logit_equality'].append(dict(case=case['id'],sha256=hashes[1],bitwise_equal=hashes[0]==hashes[1]))
113
+ assert all(r['bitwise_equal'] for r in result['logit_equality']),result['logit_equality']
114
+ result['passed']=True
115
+ print('PASS all 4217 full-vocabulary logits bitwise identical to tested candidate',flush=True)
116
+ finally:
117
+ print('PHASE restoring original W4 / 64K service',flush=True)
118
+ state=subprocess.run(['systemctl','--user','show','mindnano-ling3.service','--property=LoadState','--value'],
119
+ capture_output=True,text=True).stdout.strip()
120
+ if state=='loaded':
121
+ subprocess.run(['systemctl','--user','start','mindnano-ling3.service'],check=True)
122
+ else:
123
+ subprocess.run(['systemd-run','--user','--unit=mindnano-ling3','--collect','--property=Restart=on-failure',
124
+ '--property=RestartSec=5','--working-directory='+str(deployment),str(deployment/'mindnano-infer'),
125
+ '--context','64K','--host','0.0.0.0','--port','9091','--no-console','--log',str(deployment/'metrics-live.jsonl')],check=True)
126
+ result['health_restored']=ready(9091)
127
+ assert result['health_restored']['context_length']==65536
128
+ (output/'results.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n')
129
+ print('PASS original service restored',flush=True)
130
+
131
+
132
+ if __name__=='__main__':main()
tools/validate_weight_reclaim.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Compare deterministic API responses before/after source-weight reclamation."""
3
+ import argparse
4
+ import json
5
+ from pathlib import Path
6
+ import urllib.request
7
+
8
+ p = argparse.ArgumentParser()
9
+ p.add_argument("--url", required=True)
10
+ p.add_argument("--output", type=Path, required=True)
11
+ p.add_argument("--reference", type=Path)
12
+ p.add_argument("--boundaries", action="store_true", help="also check partial final prefill blocks")
13
+ a = p.parse_args()
14
+ client = urllib.request.build_opener(urllib.request.ProxyHandler({}))
15
+
16
+
17
+ def call(path, body=None):
18
+ req = urllib.request.Request(a.url.rstrip("/") + path,
19
+ None if body is None else json.dumps(body).encode(),
20
+ {"Content-Type": "application/json"})
21
+ with client.open(req, timeout=180) as response:
22
+ return json.load(response)
23
+
24
+
25
+ office = "请用中文列出三条会议纪要的写作建议。"
26
+ cases = [("office", office, 64), ("digits128_a", "1" * 107, 64),
27
+ ("digits128_b", "1" * 107, 64), ("digits128_c", "1" * 107, 64),
28
+ ("digits1024", "1" * 1003, 24), ("office_repeat", office, 64)]
29
+ if a.boundaries:
30
+ cases.extend((f"digits{count}", "1" * (count - 21), 16) for count in (129, 131, 255, 257))
31
+ cases.append(("office_after_boundaries", office, 64))
32
+ result = {"health_before": call("/health"), "cases": []}
33
+ for name, prompt, tokens in cases:
34
+ response = call("/v1/chat/completions", {
35
+ "model": "mindnano-ling3-tiny",
36
+ "messages": [{"role": "user", "content": prompt}],
37
+ "temperature": 0, "max_tokens": tokens, "cache_prompt": False})
38
+ result["cases"].append({"name": name, "response": response})
39
+ print(json.dumps({"case": name, "usage": response["usage"],
40
+ "metrics": response["mindnano_metrics"]}, ensure_ascii=False), flush=True)
41
+ result["health_after"] = call("/health")
42
+ a.output.parent.mkdir(parents=True, exist_ok=True)
43
+ a.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n")
44
+ assert result["cases"][0]["response"]["choices"] == result["cases"][-1]["response"]["choices"]
45
+ if a.reference:
46
+ ref = json.loads(a.reference.read_text())
47
+ assert len(ref["cases"]) == len(result["cases"])
48
+ for before, after in zip(ref["cases"], result["cases"]):
49
+ assert before["name"] == after["name"]
50
+ for field in ("choices", "usage"):
51
+ assert before["response"][field] == after["response"][field], (after["name"], field)
52
+ print("PASS: all response texts, finish reasons and token counts exactly match baseline")
tools/verify_release_archive.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Verify all release members and embedded SHA256SUMS without extracting the model."""
3
+ import argparse
4
+ import hashlib
5
+ import json
6
+ from pathlib import Path, PurePosixPath
7
+ import tarfile
8
+
9
+
10
+ def main():
11
+ p=argparse.ArgumentParser(description=__doc__)
12
+ p.add_argument('archive',type=Path)
13
+ a=p.parse_args()
14
+ expected={};actual={};roots=set();metadata={}
15
+ with tarfile.open(a.archive,'r|gz') as tar:
16
+ for member in tar:
17
+ path=PurePosixPath(member.name)
18
+ assert not path.is_absolute() and '..' not in path.parts
19
+ roots.add(path.parts[0])
20
+ if member.isdir():continue
21
+ assert member.isfile() and len(path.parts)==2,member.name
22
+ name=path.name
23
+ assert name not in actual,'duplicate member: '+name
24
+ digest=hashlib.sha256();parts=[]
25
+ with tar.extractfile(member) as f:
26
+ while block:=f.read(8*1024*1024):
27
+ digest.update(block)
28
+ if name in ('SHA256SUMS','MODEL.json','VALIDATION.json'):parts.append(block)
29
+ actual[name]=digest.hexdigest()
30
+ if name=='SHA256SUMS':
31
+ for line in b''.join(parts).decode().splitlines():
32
+ checksum,filename=line.split(' ',1)
33
+ assert filename not in expected
34
+ expected[filename]=checksum
35
+ elif parts:metadata[name]=json.loads(b''.join(parts))
36
+ assert len(roots)==1
37
+ assert expected and set(expected)==set(actual)-{'SHA256SUMS'}
38
+ for name,checksum in expected.items():assert actual[name]==checksum,name
39
+ assert metadata['VALIDATION.json']['passed']
40
+ model = metadata['MODEL.json']
41
+ if model.get('mindnano_integration_version', 0) >= 5:
42
+ assert 'qa_cache_default' in model['capabilities']
43
+ assert 'resident_session_cache' in model['capabilities']
44
+ assert metadata['VALIDATION.json']['health']['mindnano_integration_version'] >= 5
45
+ else:
46
+ assert model['all_payload_sha256_valid']
47
+ print(json.dumps(dict(archive=str(a.archive),verified_files=len(expected),
48
+ all_member_sha256_valid=True,validation_passed=True,
49
+ performance=metadata['VALIDATION.json'].get('performance')),indent=2))
50
+
51
+
52
+ if __name__=='__main__':main()