Orginize
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
/**
|
||||
* 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' || p === 'openai' || p === 'custom') 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'
|
||||
}
|
||||
|
||||
/** @type {readonly string[]} */
|
||||
var BARE_AGENT_QVAC_ONLY_KEYS = Object.freeze([
|
||||
'qvac_model',
|
||||
'qvac_profile',
|
||||
'qvac_ctx_size',
|
||||
'qvac_device',
|
||||
'qvac_main_gpu',
|
||||
'qvac_gpu_layers'
|
||||
])
|
||||
|
||||
/** @type {readonly string[]} */
|
||||
var BARE_AGENT_REST_ONLY_KEYS = Object.freeze(['rest_base_url', 'rest_api_key'])
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* id: string,
|
||||
* label: string,
|
||||
* rest_base_url: string,
|
||||
* default_model: string,
|
||||
* models: string[]
|
||||
* }} BareAgentRestProvider
|
||||
*/
|
||||
|
||||
/** @type {Record<string, BareAgentRestProvider>} */
|
||||
const BARE_AGENT_REST_PROVIDERS = {
|
||||
groq: {
|
||||
id: 'groq',
|
||||
label: 'Groq',
|
||||
rest_base_url: 'https://api.groq.com/openai/v1',
|
||||
default_model: 'llama-3.3-70b-versatile',
|
||||
models: [
|
||||
'llama-3.3-70b-versatile',
|
||||
'llama-3.1-8b-instant',
|
||||
'openai/gpt-oss-120b',
|
||||
'openai/gpt-oss-20b',
|
||||
'qwen/qwen3-32b',
|
||||
'moonshotai/kimi-k2-instruct'
|
||||
]
|
||||
},
|
||||
xai: {
|
||||
id: 'xai',
|
||||
label: 'xAI (Grok)',
|
||||
rest_base_url: 'https://api.x.ai/v1',
|
||||
default_model: 'grok-4',
|
||||
models: ['grok-4', 'grok-3', 'grok-3-mini', 'grok-3-fast', 'grok-2-1212']
|
||||
},
|
||||
openai: {
|
||||
id: 'openai',
|
||||
label: 'OpenAI',
|
||||
rest_base_url: 'https://api.openai.com/v1',
|
||||
default_model: 'gpt-4.1',
|
||||
models: ['gpt-4.1', 'gpt-4.1-mini', 'gpt-4.1-nano', 'gpt-4o', 'o4-mini']
|
||||
},
|
||||
custom: {
|
||||
id: 'custom',
|
||||
label: 'Custom OpenAI-compatible',
|
||||
rest_base_url: '',
|
||||
default_model: '',
|
||||
models: []
|
||||
}
|
||||
}
|
||||
|
||||
/** @returns {BareAgentRestProvider[]} */
|
||||
function bareAgentRestProviderList() {
|
||||
return [BARE_AGENT_REST_PROVIDERS.groq, BARE_AGENT_REST_PROVIDERS.xai, BARE_AGENT_REST_PROVIDERS.openai, BARE_AGENT_REST_PROVIDERS.custom]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [id]
|
||||
* @returns {BareAgentRestProvider}
|
||||
*/
|
||||
function bareAgentRestGetProvider(id) {
|
||||
const key = String(id || '').trim().toLowerCase()
|
||||
if (key === 'http' || key === 'rest') return BARE_AGENT_REST_PROVIDERS.custom
|
||||
return BARE_AGENT_REST_PROVIDERS[key] || BARE_AGENT_REST_PROVIDERS.groq
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [model]
|
||||
*/
|
||||
function bareAgentIsQvacModelId(model) {
|
||||
const m = String(model || '').trim()
|
||||
if (!m) return false
|
||||
return (
|
||||
/^QWEN/i.test(m) ||
|
||||
/^LLAMA_TOOL/i.test(m) ||
|
||||
/QWEN3/i.test(m) ||
|
||||
/LLAMA_TOOL_CALLING/i.test(m)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop keys that belong to the other backend so config.json matches the choice.
|
||||
* @param {Record<string, unknown>} config
|
||||
* @returns {Record<string, unknown>}
|
||||
*/
|
||||
function bareAgentSanitizeConfigForBackend(config) {
|
||||
const out = { ...(config && typeof config === 'object' ? config : {}) }
|
||||
const backend = bareAgentResolveBackend(out)
|
||||
out.backend = backend
|
||||
if (backend === 'rest') {
|
||||
for (let i = 0; i < BARE_AGENT_QVAC_ONLY_KEYS.length; i++) {
|
||||
delete out[BARE_AGENT_QVAC_ONLY_KEYS[i]]
|
||||
}
|
||||
const prov = String(out.provider || '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
if (!prov || prov === 'qvac') out.provider = 'groq'
|
||||
if (bareAgentIsQvacModelId(String(out.model || ''))) {
|
||||
const spec = bareAgentRestGetProvider(String(out.provider || 'groq'))
|
||||
if (spec.default_model) out.model = spec.default_model
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < BARE_AGENT_REST_ONLY_KEYS.length; i++) {
|
||||
delete out[BARE_AGENT_REST_ONLY_KEYS[i]]
|
||||
}
|
||||
out.provider = 'qvac'
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [secret]
|
||||
*/
|
||||
function bareAgentMaskSecretPreview(secret) {
|
||||
const t = String(secret || '')
|
||||
if (!t.trim()) return '(not set)'
|
||||
if (t.length <= 8) return '********'
|
||||
return t.slice(0, 3) + '…' + t.slice(-4)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
Reference in New Issue
Block a user