Files
bare-operating-system/packages/bare-os-coreutils/lib/agent-qvac.js
T
Raven Scott de0574a226
Release rolling / release (push) Failing after 4m54s
Enable tools by default
2026-07-31 21:17:54 -04:00

246 lines
7.5 KiB
JavaScript

/**
* 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<BareAgentQvacProfileId, BareAgentQvacProfile>} */
const BARE_AGENT_QVAC_PROFILES = {
lite: {
id: 'lite',
label: 'Lite',
description:
'Smallest download (Qwen3-0.6B). Model-card context 32k; tools enabled.',
chatModel: 'QWEN3_600M_INST_Q4',
tools: true,
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<string, unknown>} */ (t)
if (o.function && typeof o.function === 'object') {
const fn = /** @type {Record<string, unknown>} */ (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<string, unknown>} 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<string, unknown>} 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<string, unknown>} 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.
// Tools are always on for agent sessions — keep enough ctx for schemas.
if (ctx < 8192) ctx = 8192
return ctx
}
/**
* Device / main-gpu / layers for QVAC load (config + profile defaults).
* @param {Record<string, unknown>} 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
}