Files
bare-operating-system/packages/bare-os-coreutils/lib/agent-models.js
T
2026-08-18 14:49:17 -04:00

165 lines
7.3 KiB
JavaScript

/**
* Shared QVAC chat catalog + REST /models helpers (preamble for agent and discord-bot).
*/
/** @type {{ id: string, family: string, label: string, tools: boolean, ramGb: number, profile?: string }[]} */
var BARE_AGENT_QVAC_CHAT_MODELS = [
{ id: 'QWEN3_600M_INST_Q4', family: 'qwen3', label: 'Qwen3 0.6B Instruct Q4', tools: true, ramGb: 4, profile: 'lite' },
{ id: 'QWEN3_1_7B_INST_Q4', family: 'qwen3', label: 'Qwen3 1.7B Instruct Q4', tools: true, ramGb: 8, profile: 'recommended' },
{ id: 'QWEN3_4B_INST_Q4_K_M', family: 'qwen3', label: 'Qwen3 4B Instruct Q4_K_M', tools: true, ramGb: 16, profile: 'strong' },
{ id: 'QWEN3_4B_Q4_K_M', family: 'qwen3', label: 'Qwen3 4B Q4_K_M', tools: true, ramGb: 16 },
{ id: 'QWEN3_8B_INST_Q4_K_M', family: 'qwen3', label: 'Qwen3 8B Instruct Q4_K_M', tools: true, ramGb: 24 },
{ id: 'LLAMA_TOOL_CALLING_1B_INST_Q4_K', family: 'llama', label: 'Llama 3.2 1B tool-calling', tools: true, ramGb: 6, profile: 'tool-tiny' },
{ id: 'LLAMA_3_2_1B_INST_Q4_0', family: 'llama', label: 'Llama 3.2 1B Instruct Q4_0', tools: true, ramGb: 6 },
{ id: 'SMOLLM2_360M_INST_Q8', family: 'smol', label: 'SmolLM2 360M Instruct Q8', tools: false, ramGb: 3 },
{ id: 'GPT_OSS_20B_INST_Q4_K_M', family: 'gpt-oss', label: 'GPT-OSS 20B Instruct Q4_K_M', tools: true, ramGb: 24 },
{ id: 'GEMMA4_2B_MULTIMODAL_Q4_K_M', family: 'gemma', label: 'Gemma 4 2B multimodal Q4', tools: true, ramGb: 8 },
{ id: 'GEMMA4_2B_MULTIMODAL_Q6_K', family: 'gemma', label: 'Gemma 4 2B multimodal Q6', tools: true, ramGb: 10 },
{ id: 'GEMMA4_4B_MULTIMODAL_Q4_K_M', family: 'gemma', label: 'Gemma 4 4B multimodal Q4', tools: true, ramGb: 16 },
{ id: 'GEMMA4_31B_MULTIMODAL_Q4_K_M', family: 'gemma', label: 'Gemma 4 31B multimodal Q4', tools: true, ramGb: 48 },
{ id: 'QWEN3VL_2B_MULTIMODAL_Q4_K', family: 'qwen3', label: 'Qwen3-VL 2B multimodal Q4', tools: true, ramGb: 10 },
{ id: 'QWEN3_5_2B_MULTIMODAL_Q4_K_M', family: 'qwen3.5', label: 'Qwen3.5 2B multimodal Q4', tools: true, ramGb: 10 },
{ id: 'QWEN3_5_4B_MULTIMODAL_Q4_K_M', family: 'qwen3.5', label: 'Qwen3.5 4B multimodal Q4', tools: true, ramGb: 16 },
{ id: 'QWEN3_5_0_8B_MULTIMODAL_Q4_K_M', family: 'qwen3.5', label: 'Qwen3.5 8B multimodal Q4', tools: true, ramGb: 24 },
{ id: 'QWEN3_5_9B_MULTIMODAL_Q4_K_M', family: 'qwen3.5', label: 'Qwen3.5 9B multimodal Q4', tools: true, ramGb: 28 },
{ id: 'QWEN3_6_27B_MULTIMODAL_Q4_K_XL', family: 'large', label: 'Qwen3.6 27B multimodal Q4', tools: true, ramGb: 48 }
]
/** @type {Record<string, string[]>} */
var BARE_AGENT_REST_MODEL_FALLBACKS = {
groq: [
'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',
'meta-llama/llama-4-scout-17b-16e-instruct',
'meta-llama/llama-4-maverick-17b-128e-instruct',
'groq/compound'
],
xai: ['grok-4', 'grok-3', 'grok-3-mini', 'grok-3-fast', 'grok-2-1212', 'grok-2-vision-1212'],
openai: ['gpt-4.1', 'gpt-4.1-mini', 'gpt-4.1-nano', 'gpt-4o', 'gpt-4o-mini', 'o4-mini', 'o3'],
custom: []
}
/**
* @param {string} [profileId]
*/
function bareAgentQvacModelForProfile(profileId) {
const id = String(profileId || '').trim().toLowerCase()
for (let i = 0; i < BARE_AGENT_QVAC_CHAT_MODELS.length; i++) {
if (BARE_AGENT_QVAC_CHAT_MODELS[i].profile === id) return BARE_AGENT_QVAC_CHAT_MODELS[i]
}
return BARE_AGENT_QVAC_CHAT_MODELS[1] || BARE_AGENT_QVAC_CHAT_MODELS[0]
}
/**
* @param {string} [modelId]
*/
function bareAgentQvacFindChatModel(modelId) {
const id = String(modelId || '').trim()
for (let i = 0; i < BARE_AGENT_QVAC_CHAT_MODELS.length; i++) {
if (BARE_AGENT_QVAC_CHAT_MODELS[i].id === id) return BARE_AGENT_QVAC_CHAT_MODELS[i]
}
return null
}
/**
* @param {{ id: string, label?: string, family?: string }[]} models
* @param {{ family?: string, query?: string }} [opts]
*/
function bareAgentFilterModelList(models, opts) {
const o = opts && typeof opts === 'object' ? opts : {}
const fam = String(o.family || 'all').trim().toLowerCase()
const q = String(o.query || '').trim().toLowerCase()
const rows = Array.isArray(models) ? models : []
/** @type {{ id: string, label?: string, family?: string }[]} */
const out = []
for (let i = 0; i < rows.length; i++) {
const m = rows[i]
if (!m || !m.id) continue
if (fam && fam !== 'all' && String(m.family || '').toLowerCase() !== fam) continue
if (q) {
const blob = (String(m.id) + ' ' + String(m.label || '')).toLowerCase()
if (blob.indexOf(q) === -1) continue
}
out.push(m)
}
return out
}
/**
* @param {unknown} json
* @returns {{ id: string, label: string, family: string, owned_by: string }[]}
*/
function bareAgentParseOpenAiModels(json) {
const raw =
json && typeof json === 'object' && Array.isArray(/** @type {{ data?: unknown }} */ (json).data)
? /** @type {{ data: unknown[] }} */ (json).data
: Array.isArray(json)
? json
: []
const skip = /embed|whisper|tts|dall-e|davinci|babbage|audio|moderation|realtime|image|sora/i
/** @type {{ id: string, label: string, family: string, owned_by: string }[]} */
const out = []
const seen = Object.create(null)
for (let i = 0; i < raw.length; i++) {
const row = raw[i] && typeof raw[i] === 'object' ? /** @type {Record<string, unknown>} */ (raw[i]) : null
if (!row) continue
const id = String(row.id || row.name || '').trim()
if (!id || seen[id] || skip.test(id)) continue
seen[id] = 1
const owned = String(row.owned_by || row.ownedBy || '')
out.push({
id: id,
label: id,
family: owned || 'api',
owned_by: owned
})
}
out.sort(function (a, b) {
return a.id.localeCompare(b.id)
})
return out
}
/**
* @param {string} [provider]
*/
function bareAgentRestModelsFallback(provider) {
const key = String(provider || 'groq').trim().toLowerCase()
const ids = BARE_AGENT_REST_MODEL_FALLBACKS[key] || BARE_AGENT_REST_MODEL_FALLBACKS.groq
return (ids || []).map(function (id) {
return { id: id, label: id, family: key, owned_by: key }
})
}
/**
* @param {(url: string, init?: object) => Promise<{ ok?: boolean, status?: number, json?: () => Promise<unknown>, text?: () => Promise<string> }>} fetchFn
* @param {{ baseUrl?: string, apiKey?: string }} opts
*/
async function bareAgentFetchRestModels(fetchFn, opts) {
const o = opts && typeof opts === 'object' ? opts : {}
const base = String(o.baseUrl || '').trim().replace(/\/+$/, '')
if (!base) throw new Error('rest_base_url required')
if (typeof fetchFn !== 'function') throw new Error('fetch unavailable')
const url = base + '/models'
/** @type {Record<string, string>} */
const headers = { Accept: 'application/json' }
const key = String(o.apiKey || '').trim()
if (key) headers.Authorization = 'Bearer ' + key
const res = await fetchFn(url, { method: 'GET', headers: headers })
if (!res || res.ok === false) {
const st = res && res.status != null ? String(res.status) : 'fetch_failed'
throw new Error('models_http_' + st)
}
let json
if (typeof res.json === 'function') json = await res.json()
else if (typeof res.text === 'function') json = JSON.parse(await res.text())
else throw new Error('models_unreadable')
const list = bareAgentParseOpenAiModels(json)
if (!list.length) throw new Error('models_empty')
return list
}