LLeMbas CLI 1.0.0
ci / check (push) Canceled after 0s

The first public release of LLeMbas CLI: a terminal coding agent and project manager for any LLM
API, with permission modes, git snapshots, memory and skills, knowledge bases, MCP, voice, and a
link to a LLeMbas instance whose web UI can work its sessions too. Signed Linux binaries for x64
and arm64.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
HomerandClaude Opus 5.5 committed 2026-10-09 21:59:03 +00:00
commit f9bad01ed7
355 files changed
+47028

No files matched your search

+79
View File
@@ -0,0 +1,79 @@
// Reasoning effort. Logic ported from LLeMbas services/chat.py and generation.py
// (© Jaroslav Beneš, MIT), where the reasons are written up at length:
// - llama.cpp silently drops a top-level `reasoning_effort`; what reaches a gpt-oss behind it is
// `chat_template_kwargs.reasoning_effort`, so by default both are sent — and only when chosen.
// - A template that does not know a value raises and fails the whole request (Bonsai refuses
// `high`), so a refusal is detected, the model's vocabulary narrowed, and the request retried once.
import { EFFORTS, type Effort } from "../config/schema.ts"
import type { ResolvedModel } from "./types.ts"
import { learned } from "./learned.ts"
export const DEFAULT_EFFORTS: Effort[] = ["low", "medium", "high"]
/** The efforts this model accepts, in offering order: learned > configured > the common three. */
export function effortsFor(m: ResolvedModel): Effort[] {
const list = learned().efforts[m.ref] ?? m.spec.efforts ?? DEFAULT_EFFORTS
return EFFORTS.filter((e) => list.includes(e))
}
/** The model's default effort, or null for off / not a reasoning model. */
export function defaultEffort(m: ResolvedModel): Effort | null {
const e = m.spec.effort
if (!e || e === "off") return null
return effortsFor(m).includes(e) ? e : null
}
/** Put the effort into an openai-chat request body. An effort outside the vocabulary is dropped. */
export function applyEffort(body: Record<string, unknown>, m: ResolvedModel, effort: Effort | null | undefined) {
if (!effort || !effortsFor(m).includes(effort)) return
const style = m.spec.effort_style ?? "both"
if (style !== "kwargs") body.reasoning_effort = effort
if (style !== "top") {
const kwargs = { ...((body.chat_template_kwargs as Record<string, unknown>) ?? {}) }
kwargs.reasoning_effort = effort
body.chat_template_kwargs = kwargs
}
}
export function stripEffort(body: Record<string, unknown>): Record<string, unknown> {
const out = { ...body }
delete out.reasoning_effort
const kwargs = { ...((out.chat_template_kwargs as Record<string, unknown>) ?? {}) }
delete kwargs.reasoning_effort
if (Object.keys(kwargs).length) out.chat_template_kwargs = kwargs
else delete out.chat_template_kwargs
return out
}
/** Whether an error is a chat template refusing the effort. Deliberately narrow. */
export function effortRefused(message: string): boolean {
const m = message.toLowerCase()
return m.includes("effort") && (m.includes("unexpected") || m.includes("supported"))
}
/** Efforts an error message advertises ("Supported types are xhigh (default), medium, and low"). */
export function advertisedEfforts(message: string): Effort[] {
const m = message.toLowerCase()
const at = m.indexOf("supported")
if (at === -1) return []
// Whole words: "high" is a substring of "xhigh".
const words = new Set(m.slice(at).match(/[a-z]+/g) ?? [])
return EFFORTS.filter((e) => words.has(e))
}
/** Read the accepted efforts out of a Jinja chat template (llama-server /props). ≥2 or nothing. */
export function effortsFromTemplate(template: string): Effort[] {
if (!template || !template.includes("reasoning_effort")) return []
const found = new Set<string>()
for (const match of template.matchAll(/reasoning_effort/g)) {
let window = template.slice(match.index, match.index + 400)
if (window.includes("%}")) window = window.split("%}")[0]!
for (const lit of window.matchAll(/['"]([a-z]{3,8})['"]/g)) found.add(lit[1]!)
}
for (const group of template.matchAll(/[[(]((?:\s*['"][a-z]{3,8}['"]\s*,?)+)[\])]/g)) {
const lits = [...group[1]!.matchAll(/['"]([a-z]{3,8})['"]/g)].map((x) => x[1]!)
if (lits.length >= 2 && lits.every((v) => (EFFORTS as readonly string[]).includes(v))) lits.forEach((v) => found.add(v))
}
const out = EFFORTS.filter((e) => found.has(e))
return out.length >= 2 ? out : []
}