spike(llm): T18 端侧小模型——MNN native 骨架 + 局域网模型服务
- cpp/:liblocalllm.so NAPI 骨架(loadModel/chat/benchmark/release),预置 libMNN.so 3.6.1 鸿蒙包(已验证含 createLLM 符号;.gitignore 挡预编译库与 models/,获取方式见任务文档) - LocalLlm.ets:native 调用的 ets 封装;LlmTestPage:模型从局域网服务器下载→加载→首 token/速度 benchmark - scripts/serve-models.mjs:开发期局域网模型文件服务器(Qwen3-0.6B-MNN 456MB,SHA256 与 ModelScope 官方清单逐文件核对) - build-profile.json5 接 externalNativeOptions(arm64-v8a);ProfileContent 加「端侧LLM调试」入口(内部调试用) - docs/开发任务包/T18-端侧小模型SPIKE.md:目标/验证项/回退线;真机 benchmark 数据回来后写 T18-REPORT 三问三答
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* T18 调试期局域网模型服务器(零依赖,Node 18+)。
|
||||
*
|
||||
* 用法:node scripts/serve-models.mjs [端口=8080]
|
||||
* 手机与电脑连同一 Wi-Fi,App 内模型地址填 http://<电脑局域网IP>:<端口>
|
||||
*
|
||||
* 服务内容:models/ 目录下的 manifest.json 与模型文件。
|
||||
* 调试阶段一次性整包下载即可;断点续传(Range)留待商用阶段接 CDN 时再做。
|
||||
*/
|
||||
import { createServer } from 'node:http';
|
||||
import { createReadStream, existsSync, statSync } from 'node:fs';
|
||||
import { networkInterfaces } from 'node:os';
|
||||
import { extname, join, normalize } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = normalize(join(fileURLToPath(import.meta.url), '..', '..', 'models'));
|
||||
const port = Number(process.argv[2] ?? 8080);
|
||||
|
||||
const MIME = {
|
||||
'.json': 'application/json; charset=utf-8',
|
||||
'.txt': 'text/plain; charset=utf-8',
|
||||
'.mnn': 'application/octet-stream',
|
||||
'.weight': 'application/octet-stream',
|
||||
};
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
const urlPath = decodeURIComponent((req.url ?? '/').split('?')[0]);
|
||||
// 防目录穿越:规范化后必须仍落在 root 内
|
||||
const filePath = normalize(join(root, urlPath));
|
||||
if (!filePath.startsWith(root) || !existsSync(filePath) || !statSync(filePath).isFile()) {
|
||||
res.writeHead(404).end('not found');
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, {
|
||||
'Content-Type': MIME[extname(filePath)] ?? 'application/octet-stream',
|
||||
'Content-Length': statSync(filePath).size,
|
||||
});
|
||||
createReadStream(filePath).pipe(res);
|
||||
});
|
||||
|
||||
server.listen(port, '0.0.0.0', () => {
|
||||
console.log(`serving ${root}`);
|
||||
for (const [name, addrs] of Object.entries(networkInterfaces())) {
|
||||
for (const a of addrs ?? []) {
|
||||
if (a.family === 'IPv4' && !a.internal) {
|
||||
console.log(` http://${a.address}:${port}/ (${name})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user