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>
This commit is contained in:
Lauren ten Hoor
2026-02-12 20:37:15 +08:00
parent 84483176f4
commit b2fc94db9e
12 changed files with 835 additions and 304 deletions

View File

@@ -0,0 +1,136 @@
/**
* autoconfigure-models.ts — Tool for automatically configuring model assignments.
*
* Queries available authenticated models and intelligently assigns them to DevClaw roles.
*/
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { jsonResult } from "openclaw/plugin-sdk";
import type { ToolContext } from "../types.js";
import {
assignModels,
formatAssignment,
generateSetupInstructions,
type ModelAssignment,
} from "../setup/smart-model-selector.js";
import { fetchAuthenticatedModels } from "../setup/model-fetcher.js";
/**
* Get available authenticated models from OpenClaw.
*/
async function getAuthenticatedModels(
api: OpenClawPluginApi,
): Promise<Array<{ model: string; provider: string; authenticated: boolean }>> {
try {
const models = fetchAuthenticatedModels();
// Map to the format expected by assignModels()
return models.map((m) => {
// Extract provider from key (format: provider/model-name)
const provider = m.key.split("/")[0] || "unknown";
return {
model: m.key,
provider,
authenticated: true,
};
});
} catch (err) {
throw new Error(`Failed to get authenticated models: ${(err as Error).message}`);
}
}
/**
* Create the autoconfigure_models tool.
*/
export function createAutoConfigureModelsTool(api: OpenClawPluginApi) {
return (ctx: ToolContext) => ({
name: "autoconfigure_models",
label: "Auto-Configure Models",
description:
"Automatically discover authenticated models and intelligently assign them to DevClaw roles based on capability tiers",
parameters: {
type: "object",
properties: {
preferProvider: {
type: "string",
description:
"Optional: Prefer models from this provider (e.g., 'anthropic', 'openai')",
},
},
},
async execute(_id: string, params: Record<string, unknown>) {
try {
// Get all authenticated models
let authenticatedModels = await getAuthenticatedModels(api);
// Filter by preferred provider if specified
const preferProvider = params?.preferProvider as string | undefined;
if (preferProvider) {
const filtered = authenticatedModels.filter(
(m) => m.provider.toLowerCase() === preferProvider.toLowerCase(),
);
if (filtered.length === 0) {
return jsonResult({
success: false,
error: `No authenticated models found for provider: ${preferProvider}`,
message: `❌ No authenticated models found for provider "${preferProvider}".\n\nAvailable providers: ${[...new Set(authenticatedModels.map((m) => m.provider))].join(", ")}`,
});
}
authenticatedModels = filtered;
}
// Intelligently assign models using current session context
const assignment = await assignModels(authenticatedModels, ctx.sessionKey);
if (!assignment) {
// No models available
const instructions = generateSetupInstructions();
return jsonResult({
success: false,
modelCount: 0,
message: instructions,
});
}
// Format the assignment
const table = formatAssignment(assignment);
const modelCount = authenticatedModels.length;
let message = `✅ Auto-configured models based on ${modelCount} authenticated model${modelCount === 1 ? "" : "s"}:\n\n`;
message += table;
message += "\n\n";
if (modelCount === 1) {
message += " Only one authenticated model found — assigned to all roles.";
} else {
message += " Models assigned by capability tier (Tier 1 → senior, Tier 2 → medior/reviewer, Tier 3 → junior/tester).";
}
if (preferProvider) {
message += `\n📌 Filtered to provider: ${preferProvider}`;
}
message += "\n\n**Next step:** Pass this configuration to `setup` tool:\n";
message += "```javascript\n";
message += "setup({ models: <this-configuration> })\n";
message += "```";
return jsonResult({
success: true,
modelCount,
assignment,
models: assignment,
provider: preferProvider || "auto",
message,
});
} catch (err) {
const errorMsg = (err as Error).message;
api.logger.error(`Auto-configure models error: ${errorMsg}`);
return jsonResult({
success: false,
error: errorMsg,
message: `❌ Failed to auto-configure models: ${errorMsg}`,
});
}
},
});
}

View File

@@ -112,9 +112,10 @@ export function createSetupTool(api: OpenClawPluginApi) {
...DEV_LEVELS.map((t) => ` dev.${t}: ${result.models.dev[t]}`),
...QA_LEVELS.map((t) => ` qa.${t}: ${result.models.qa[t]}`),
"",
"Files:",
...result.filesWritten.map((f) => ` ${f}`),
);
lines.push("Files:", ...result.filesWritten.map((f) => ` ${f}`));
if (result.warnings.length > 0)
lines.push("", "Warnings:", ...result.warnings.map((w) => ` ${w}`));
lines.push(