@ aparte/ 提供者- 翻译器
运行 LLMs 浏览器中100% 通过 变形器.js (WebGPU,有WASM倒置)——没有API,没有密钥,没有服务器. 推论从主线流出 (单位:千美元) 网络工作者将神迹流入歧途.
npm install @aparte/provider-transformers @huggingface/transformers
@huggingface/transformers 是一个 同伴依赖 - 你带来你想要的版本(它很重
(原始内容存档于2013-10-12). ship its own onnxruntime. @aparte/core 是一个 同伴依赖。 。 。 .
import { aparteGlobalConfig, AparteDirectTransport } from '@aparte/core';
import { TransformersProvider, registerModel } from '@aparte/provider-transformers';
registerModel({
id: 'onnx-community/Qwen2.5-0.5B-Instruct',
name: 'Qwen2.5 0.5B',
task: 'text-generation',
capabilities: ['streaming'],
dtype: 'q4',
});
aparteGlobalConfig.registerAIProvider(TransformersProvider);
aparteGlobalConfig.setTransport(new AparteDirectTransport({ byok: true }));
供应商拥有其I/O(它在当地运行推论),所以 AparteDirectTransport 只有代表参加.
模式加权下载一次并在缓存API中持续; prepareModel 汇报进展情况,以及
listCachedModels 编号 : deleteCachedModel 管理磁盘缓存.
跑者——什么是装入模型
那个工人经营着 运行:装入一个模型并驱动它的部件. 有两艘船
软件包,用 task;第三个选项是您自己的模块.
task | 负载 | 页:1 |
|---|---|---|
'text-generation' (默认) | pipeline('text-generation') | 任何聊天模式——Quen, SmolLM, Llama, Phi.. |
'image-text-to-text' | AutoProcessor + 键 AutoModelForImageTextToText | 愿景模型——不管是什么 AutoModelForImageTextToText 解析度(SmolVLM、Qwen2-VL、LFM2-VL、Gemma 3...);在SmolVLM上测量 |
视觉模型读取信息中的图像部分({ type: 'image', image: dataUrl }没错
正如作曲家所赋予的:
registerModel({
id: 'HuggingFaceTB/SmolVLM-256M-Instruct',
name: 'SmolVLM 256M',
task: 'image-text-to-text',
capabilities: ['streaming', 'vision'],
// Three ONNX parts, three dtypes — the shape the model card recommends for WebGPU.
dtype: { embed_tokens: 'fp16', vision_encoder: 'q4', decoder_model_merged: 'q4' },
});
测量出:在WebGPU(Chrimium,AMD Radeon 8060S)上的SmolVLM-256M:第一个负载为7 s(包含下载),第一个代号为3.7 s cold;停止打断模型,而不仅仅是读取.
每个跑者都是自己的块,只有在模型要求时才装入. 都放下这个工具
- 要求助手转身 tool 转过来,用一个
警告( 工具语法是每个模范家族); 助手是什么 说 在打电话之前 留在
快点 文本运行器 说,当它掉出一个图像:它从不回答无法回答的照片
似乎它有, 和视觉跑者计算的内容部分 它不能携带的方式相同.
自己一个跑者
点 runner 在出口的ES模块中 createRunner; 它获胜 task工人
将它导入,并交出同样的变形器。 js 实例内置使用, 所以研究级
型号——自定义视觉塔,运行时你交换的适配器——适合不放弃
提供者的工作者, 队列, 进度, 取消和缓存 :
registerModel({ id: 'my-org/my-model', name: 'Mine', capabilities: ['streaming'], runner: './runners/mine.js' });
// runners/mine.js — served by your app; the worker imports it by URL
export async function createRunner(ctx) {
// ctx.transformers is the Transformers.js the page installed or mapped — never import your own
const { AutoTokenizer, AutoModelForCausalLM, TextStreamer, InterruptableStoppingCriteria } = ctx.transformers;
const tokenizer = await AutoTokenizer.from_pretrained(ctx.modelId);
const model = await AutoModelForCausalLM.from_pretrained(ctx.modelId, { dtype: ctx.dtype, device: ctx.device });
return {
async generate({ messages, options, emit, signal }) {
if (signal.aborted) return;
// messages arrive WITH their content parts — render them the way this model wants;
// this one reads text, so the parts are flattened and tool turns left out
const turns = messages
.filter((m) => m.role === 'user' || m.role === 'assistant' || m.role === 'system')
.map((m) => ({ role: m.role, content: typeof m.content === 'string' ? m.content : m.content.filter((p) => p.type === 'text').map((p) => p.text).join('') }));
const inputs = tokenizer.apply_chat_template(turns, { add_generation_prompt: true, return_dict: true });
const streamer = new TextStreamer(tokenizer, {
skip_prompt: true, skip_special_tokens: true,
callback_function: (text) => emit({ type: 'text', delta: text }),
});
// Stop: without this the model generates on to its ceiling, and the next reply waits behind it
const stopping = new InterruptableStoppingCriteria();
signal.addEventListener('abort', () => stopping.interrupt(), { once: true });
await model.generate({ ...inputs, max_new_tokens: options.maxTokens ?? 8192, streamer, stopping_criteria: stopping });
// `done` is optional — the provider closes the stream when generate() resolves
},
// anything that is not a generation: the provider passes it through, untouched
async command(name, payload) { if (name === 'adapter') { /* swap a LoRA, warm a cache… */ } },
dispose() { model.dispose(); },
};
}
input.tools 请求中允许的工具(a) RunnerTool数字 : name, (中文(简体) ), description, (中文(简体) ),
inputSchema和 input.toolChoice 联合国 'auto' 编号 : 'none' 编号 : { name } ——无时缺席.
emit 说出分流语词汇(text, (中文(简体) ), thinking, (中文(简体) ), tool_use, (中文(简体) ), done, (中文(简体) ), error, (中文(简体) ),
signal 停止开火, ctx.progress 联合国 RunnerProgress) (中文(简体) ) ctx.warn 到达页面。 合同是
导出 : TransformersRunner, (中文(简体) ), RunnerContext, (中文(简体) ), RunnerGenerateInput, (中文(简体) ), RunnerTool, (中文(简体) ), CreateRunner, (中文(简体) ), RunnerModule (怎样)
模块导出, BuiltInRunner (两者) task 名称)和 TransformersModule (类型
ctx.transformers) (中文(简体) ).
runnerCommand(modelId, name, payload) 到达跑道上 command() 从页面起,队列
飞行中产生物的后方.
每分页一条管道
不像其他不同的供货商 这家的状态 选项卡范围,而不是聊天范围页:1
工人,一个装满的模型,一个一个生成一次. setComputeDevice, (中文(简体) ), setMaxCachedModels 财务报告和审定财务报表
setHardwareTierModels 设置为整页.
这就是资源谈论,而不是设计上的偏好——本地模式是1到2GB的权重和 一个WebGPU管道, 所以每个聊天的工人将意味着一个标签中的N副本。 两个聊天 页:1 不变 模型就是这个例子: 他们分享负载,免费.
两个聊天 不同 模式就是成本。 他们在一个管道上串接, 所以每个转弯都卸下一个模型 装上另一个模型 缓存预算对此作了补充: 有很多模型留下来 下载 在浏览器的缓存 API (磁盘,不是内存: 一个模型是 无论预算何时加载),默认时,一个开关可以删除另一个开关 模型的重量,再下载一次。 提供商看到后警告一次:
import { setMaxCachedModels, getMaxCachedModels } from '@aparte/provider-transformers';
setMaxCachedModels(2); // keep both downloaded, if the browser's storage has room
setMaxCachedModels(0); // no limit — the browser's storage quota is the only bound
getMaxCachedModels(); // the current budget
范围: 文本和视觉模型通过内置跑道, 任何其它通过 自己的跑者; 只浏览器 (与其他供应商不同——它需要WebGPU/WASM, 工人和Cache API,所以它是一个不运行于节点的适配器. 工具调用 对于本地模型来说,模型是特定的:内置的跑者放弃了工具调用及其结果—— 呼叫一个助手转弯
tool转过来,回答他们,不是助理说的 在他们之前,有一个控制台警告他们;一个自定义的跑者,可能使他们得势。 部分 独立 单相机 仅限无害环境管理. 见 供应商 用于全部使用的文档中的指南.