woolball-client
Version:
Client-side library for Woolball enabling secure browser resource sharing for distributed AI task processing
190 lines (189 loc) • 8.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerWebMcpTools = registerWebMcpTools;
exports.unregisterWebMcpTools = unregisterWebMcpTools;
exports.isWebMcpAvailable = isWebMcpAvailable;
exports.isWebMcpRegistered = isWebMcpRegistered;
require("@mcp-b/global");
const index_js_1 = require("../utils/tasks/index.js");
function toMcpResult(result) {
if (result.error) {
return {
content: [{ type: 'text', text: String(result.error) }],
isError: true,
};
}
return {
content: [{ type: 'text', text: JSON.stringify(result) }],
};
}
function buildToolDefinitions() {
return [
{
name: 'automatic-speech-recognition',
description: 'Transcribe audio to text using Whisper models. Runs entirely in your browser via WebGPU/WASM.',
inputSchema: {
type: 'object',
properties: {
input: { type: 'string', description: 'Base64-encoded WAV audio' },
model: { type: 'string', description: 'HuggingFace model ID' },
dtype: { type: 'string', description: 'Quantization type' },
return_timestamps: { type: 'string', description: 'Return word-level timestamps' },
},
required: ['input'],
},
annotations: { readOnlyHint: true },
async execute(args) {
const data = {
input: args.input,
model: args.model || 'onnx-community/whisper-large-v3-turbo_timestamped',
dtype: args.dtype || 'q8',
return_timestamps: args.return_timestamps === 'true' || args.return_timestamps === true,
};
const result = await index_js_1.taskProcessors['automatic-speech-recognition'](data);
return toMcpResult(result);
},
},
{
name: 'text-to-speech',
description: 'Convert text to speech audio. Supports Transformers.js (MMS) and Kokoro providers with multiple voices.',
inputSchema: {
type: 'object',
properties: {
input: { type: 'string', description: 'Text to synthesize' },
model: { type: 'string', description: 'HuggingFace model ID' },
dtype: { type: 'string', description: 'Quantization type' },
provider: { type: 'string', description: 'TTS provider' },
voice: { type: 'string', description: 'Voice name (Kokoro provider only)' },
},
required: ['input'],
},
annotations: { readOnlyHint: true },
async execute(args) {
const data = {
input: args.input,
model: args.model || 'facebook/mms-tts-eng',
dtype: args.dtype || 'q8',
provider: args.provider || 'transformers',
voice: args.voice,
};
const result = await index_js_1.taskProcessors['text-to-speech'](data);
if (result.audio) {
return { content: [{ type: 'resource', data: result.audio, mimeType: 'audio/wav' }] };
}
return toMcpResult(result);
},
},
{
name: 'translation',
description: 'Translate text between 200+ languages using NLLB. Runs entirely in your browser.',
inputSchema: {
type: 'object',
properties: {
input: { type: 'string', description: 'Text to translate' },
model: { type: 'string', description: 'HuggingFace model ID' },
srcLang: { type: 'string', description: 'Source language code (FLORES200 format, e.g. eng_Latn)' },
tgtLang: { type: 'string', description: 'Target language code (FLORES200 format, e.g. por_Latn)' },
dtype: { type: 'string', description: 'Quantization type' },
},
required: ['input', 'srcLang', 'tgtLang'],
},
annotations: { readOnlyHint: true },
async execute(args) {
const data = {
input: args.input,
model: args.model || 'Xenova/nllb-200-distilled-600M',
srcLang: args.srcLang,
tgtLang: args.tgtLang,
dtype: args.dtype || 'q8',
};
const result = await index_js_1.taskProcessors['translation'](data);
return toMcpResult(result);
},
},
{
name: 'text-generation',
description: 'Generate text using LLMs (SmolLM2, Qwen2.5, DeepSeek R1, Llama). Supports Transformers.js, WebLLM, and MediaPipe.',
inputSchema: {
type: 'object',
properties: {
input: { type: 'string', description: 'JSON array of messages [{role, content}]' },
model: { type: 'string', description: 'Model ID' },
dtype: { type: 'string', description: 'Quantization type' },
provider: { type: 'string', description: 'LLM provider' },
max_new_tokens: { type: 'string', description: 'Maximum tokens to generate' },
temperature: { type: 'string', description: 'Sampling temperature' },
do_sample: { type: 'string', description: 'Enable sampling' },
},
required: ['input'],
},
annotations: { readOnlyHint: true },
async execute(args) {
const data = {
input: args.input,
model: args.model || 'HuggingFaceTB/SmolLM2-135M-Instruct',
dtype: args.dtype || 'q4f16',
provider: args.provider || 'transformers',
max_new_tokens: Number(args.max_new_tokens) || 250,
temperature: Number(args.temperature) || 1.0,
do_sample: args.do_sample === 'true' || args.do_sample === true,
};
const result = await index_js_1.taskProcessors['text-generation'](data);
return toMcpResult(result);
},
},
{
name: 'image-text-to-text',
description: 'Describe or answer questions about images using multimodal vision models. Runs in your browser.',
inputSchema: {
type: 'object',
properties: {
input: { type: 'string', description: 'JSON string of {image: base64, text: question}' },
model: { type: 'string', description: 'Model ID' },
dtype: { type: 'string', description: 'Quantization type' },
max_new_tokens: { type: 'string', description: 'Maximum tokens to generate' },
do_sample: { type: 'string', description: 'Enable sampling' },
},
required: ['input'],
},
annotations: { readOnlyHint: true },
async execute(args) {
const data = {
input: args.input,
model: args.model || 'llava-hf/llava-onevision-qwen2-0.5b-ov-hf',
dtype: args.dtype || 'q4f16',
max_new_tokens: Number(args.max_new_tokens) || 64,
do_sample: args.do_sample === 'true' || args.do_sample === true,
};
const result = await index_js_1.taskProcessors['image-text-to-text'](data);
return toMcpResult(result);
},
},
];
}
let registered = false;
function registerWebMcpTools() {
if (registered)
return true;
if (!navigator.modelContext)
return false;
for (const tool of buildToolDefinitions()) {
navigator.modelContext.registerTool(tool);
}
registered = true;
return true;
}
function unregisterWebMcpTools() {
if (!registered || !navigator.modelContext)
return;
for (const tool of buildToolDefinitions()) {
navigator.modelContext.unregisterTool(tool.name);
}
registered = false;
}
function isWebMcpAvailable() {
return !!navigator.modelContext;
}
function isWebMcpRegistered() {
return registered;
}