/** * QVAC helpers for /bin/agent (no SDK import — host bridge via ctx.bareOsQvac*). */ /** @typedef {'lite'|'recommended'|'strong'|'tool-tiny'} BareAgentQvacProfileId */ /** * Native context windows from upstream model cards (not conservative laptop defaults). * Qwen3 dense (0.6B/1.7B/4B): https://huggingface.co/Qwen/Qwen3-0.6B — 32,768 * Llama 3.2 1B (tool-calling finetune base): Meta Llama 3.2 — 128,000 * Absolute ceiling for overrides / host clamp. */ const BARE_AGENT_QVAC_CTX_QWEN3 = 32768 const BARE_AGENT_QVAC_CTX_LLAMA32_1B = 131072 const BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX = 131072 /** * @typedef {{ * id: BareAgentQvacProfileId, * label: string, * description: string, * chatModel: string, * tools: boolean, * minRamGb: number, * minDiskGb: number, * approxDownloadGb: number, * ctxSize: number * }} BareAgentQvacProfile */ /** @type {Record} */ const BARE_AGENT_QVAC_PROFILES = { lite: { id: 'lite', label: 'Lite', description: 'Smallest download (Qwen3-0.6B). Model-card context 32k; limited tool use.', chatModel: 'QWEN3_600M_INST_Q4', tools: false, minRamGb: 4, minDiskGb: 2, approxDownloadGb: 0.5, ctxSize: BARE_AGENT_QVAC_CTX_QWEN3 }, recommended: { id: 'recommended', label: 'Recommended', description: 'Best balance for Bare OS agent tool calling (Qwen3-1.7B, model-card context 32k).', chatModel: 'QWEN3_1_7B_INST_Q4', tools: true, minRamGb: 8, minDiskGb: 5, approxDownloadGb: 2.5, ctxSize: BARE_AGENT_QVAC_CTX_QWEN3 }, strong: { id: 'strong', label: 'Strong', description: 'Better reasoning (Qwen3-4B). Model-card context 32k (131k with YaRN not enabled).', chatModel: 'QWEN3_4B_INST_Q4_K_M', tools: true, minRamGb: 16, minDiskGb: 8, approxDownloadGb: 3.5, ctxSize: BARE_AGENT_QVAC_CTX_QWEN3 }, 'tool-tiny': { id: 'tool-tiny', label: 'Tool-tiny', description: 'Llama 3.2 1B tool-calling fallback (model-card context 128k).', chatModel: 'LLAMA_TOOL_CALLING_1B_INST_Q4_K', tools: true, minRamGb: 6, minDiskGb: 3, approxDownloadGb: 1, ctxSize: BARE_AGENT_QVAC_CTX_LLAMA32_1B } } /** * Model-card / train context for a QVAC registry id (fallback 32k). * @param {string} [modelId] * @returns {number} */ function bareAgentQvacModelCardCtxSize(modelId) { const id = String(modelId || '') .trim() .toUpperCase() if (!id) return BARE_AGENT_QVAC_CTX_QWEN3 if (id.includes('LLAMA') || id.includes('LLAMA_TOOL')) { return BARE_AGENT_QVAC_CTX_LLAMA32_1B } if (id.includes('QWEN3')) return BARE_AGENT_QVAC_CTX_QWEN3 return BARE_AGENT_QVAC_CTX_QWEN3 } /** @returns {BareAgentQvacProfile[]} */ function bareAgentQvacProfileList() { return Object.values(BARE_AGENT_QVAC_PROFILES) } /** * @param {string} [id] * @returns {BareAgentQvacProfile} */ function bareAgentQvacGetProfile(id) { const key = String(id || '').trim().toLowerCase() return BARE_AGENT_QVAC_PROFILES[key] || BARE_AGENT_QVAC_PROFILES.recommended } /** * Flatten OpenAI nested tool defs → QVAC flat shape. * @param {unknown[]} tools * @returns {unknown[]} */ function bareAgentFlattenToolsForQvac(tools) { if (!Array.isArray(tools)) return [] /** @type {unknown[]} */ const out = [] for (const t of tools) { if (!t || typeof t !== 'object') continue const o = /** @type {Record} */ (t) if (o.function && typeof o.function === 'object') { const fn = /** @type {Record} */ (o.function) out.push({ type: 'function', name: typeof fn.name === 'string' ? fn.name : '', description: typeof fn.description === 'string' ? fn.description : '', parameters: fn.parameters && typeof fn.parameters === 'object' ? fn.parameters : { type: 'object', properties: {} } }) continue } if (typeof o.name === 'string') { out.push({ type: 'function', name: o.name, description: typeof o.description === 'string' ? o.description : '', parameters: o.parameters && typeof o.parameters === 'object' ? o.parameters : { type: 'object', properties: {} } }) } } return out.filter((x) => x && typeof x === 'object' && /** @type {any} */ (x).name) } /** * @param {Record} ctx * @returns {boolean} */ function bareAgentQvacBridgeAvailable(ctx) { if (!ctx || typeof ctx !== 'object') return false if (typeof ctx.bareOsQvacAvailable === 'function') { try { return Boolean(ctx.bareOsQvacAvailable()) } catch { return false } } return typeof ctx.bareOsQvacComplete === 'function' } /** * Normalize backend from config (qvac | rest). * @param {Record} config * @returns {'qvac'|'rest'} */ function bareAgentResolveBackend(config) { const b = String(config?.backend || '').trim().toLowerCase() if (b === 'rest' || b === 'openai' || b === 'http') return 'rest' if (b === 'qvac') return 'qvac' const p = String(config?.provider || '').trim().toLowerCase() if (p === 'qvac') return 'qvac' if (p === 'groq' || p === 'xai') return 'rest' // Default: qvac when key unset; rest when key present (legacy configs) if (config?.rest_api_key && String(config.rest_api_key).trim()) return 'rest' return 'qvac' } /** * Resolve context window for QVAC load from the model card / profile. * Legacy undersized `qvac_ctx_size` values (e.g. 4096/8192) are ignored so * setup stays automatic; only overrides ≥ the profile default apply. * Host may still cap via `BARE_OS_QVAC_MAX_CTX`. * @param {Record} config * @param {BareAgentQvacProfile} profile */ function bareAgentQvacResolveCtxSize(config, profile) { const modelId = String( (config && (config.qvac_model || config.model)) || (profile && profile.chatModel) || '' ) const cardCtx = bareAgentQvacModelCardCtxSize(modelId) const profileCtx = Math.max( 2048, Number(profile && profile.ctxSize) || cardCtx ) let ctx = Math.min( BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX, Math.max(profileCtx, cardCtx) ) const raw = Number(config && config.qvac_ctx_size) if (Number.isFinite(raw) && raw >= profileCtx) { ctx = Math.min(BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX, Math.floor(raw)) } // Full Bare OS tool list ≈ 4.5k tokens; leave room for system + reply. if (profile && profile.tools !== false && ctx < 8192) ctx = 8192 return ctx } /** * Device / main-gpu / layers for QVAC load (config + profile defaults). * @param {Record} config * @returns {{ device?: string, mainGpu?: string | number, gpuLayers?: number }} */ function bareAgentQvacResolveDeviceOpts(config) { /** @type {{ device?: string, mainGpu?: string | number, gpuLayers?: number }} */ const out = {} const device = String(config && config.qvac_device ? config.qvac_device : '') .trim() .toLowerCase() if (device === 'cpu' || device === 'gpu') out.device = device const mainRaw = config && config.qvac_main_gpu if (mainRaw !== undefined && mainRaw !== null && String(mainRaw).trim() !== '') { const s = String(mainRaw).trim().toLowerCase() if (s === 'auto' || s === 'dedicated' || s === 'integrated') out.mainGpu = s else if (/^\d+$/.test(s)) out.mainGpu = Number.parseInt(s, 10) } else { out.mainGpu = 'auto' } const layers = Number(config && config.qvac_gpu_layers) if (Number.isFinite(layers) && layers >= 0) out.gpuLayers = Math.floor(layers) return out }