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:
136
lib/tools/autoconfigure-models.ts
Normal file
136
lib/tools/autoconfigure-models.ts
Normal 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}`,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user