Files
devclaw-gitea/lib/setup/model-fetcher.ts
Lauren ten Hoor b2fc94db9e feat: LLM-powered model auto-configuration and improved onboarding
Major changes:
- Add autoconfigure_models tool for intelligent model assignment
- Implement LLM-based model selection using openclaw agent
- Improve onboarding flow with better model access checks
- Update README with clearer installation and onboarding instructions

Technical improvements:
- Add model-fetcher utility to query authenticated models
- Add smart-model-selector for LLM-driven model assignment
- Use session context for LLM calls during onboarding
- Suppress logging from openclaw models list calls

Documentation:
- Add prerequisites section to README
- Add conversational onboarding example
- Improve quick start flow

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 20:37:15 +08:00

82 lines
2.3 KiB
TypeScript

/**
* model-fetcher.ts — Shared helper for fetching OpenClaw models without logging.
*
* Uses execSync to bypass OpenClaw's command logging infrastructure.
*/
import { execSync } from "node:child_process";
export type OpenClawModelRow = {
key: string;
name?: string;
input: string;
contextWindow: number | null;
local: boolean;
available: boolean;
tags: string[];
missing?: boolean;
};
/**
* Fetch all models from OpenClaw without logging.
*
* @param allModels - If true, fetches all models (--all flag). If false, only authenticated models.
* @returns Array of model objects from OpenClaw's model registry
*/
export function fetchModels(allModels = true): OpenClawModelRow[] {
try {
const command = allModels
? "openclaw models list --all --json"
: "openclaw models list --json";
// Use execSync directly to bypass OpenClaw's command logging
const output = execSync(command, {
encoding: "utf-8",
timeout: 10000,
cwd: process.cwd(),
// Suppress stderr to avoid any error messages
stdio: ["pipe", "pipe", "ignore"],
}).trim();
if (!output) {
throw new Error("Empty output from openclaw models list");
}
// Parse JSON (skip any log lines like "[plugins] ...")
const lines = output.split("\n");
// Find the first line that starts with { (the beginning of JSON)
const jsonStartIndex = lines.findIndex((line: string) => {
const trimmed = line.trim();
return trimmed.startsWith("{");
});
if (jsonStartIndex === -1) {
throw new Error(
`No JSON object found in output. Got: ${output.substring(0, 200)}...`,
);
}
// Join all lines from the JSON start to the end
const jsonString = lines.slice(jsonStartIndex).join("\n");
const data = JSON.parse(jsonString);
const models = data.models as OpenClawModelRow[];
if (!Array.isArray(models)) {
throw new Error(`Expected array of models, got: ${typeof models}`);
}
return models;
} catch (err) {
throw new Error(`Failed to fetch models: ${(err as Error).message}`);
}
}
/**
* Fetch only authenticated models (available: true).
*/
export function fetchAuthenticatedModels(): OpenClawModelRow[] {
// Use --all flag but suppress logging via stdio in fetchModels()
return fetchModels(true).filter((m) => m.available === true);
}