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>
137 lines
4.6 KiB
TypeScript
137 lines
4.6 KiB
TypeScript
/**
|
||
* 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}`,
|
||
});
|
||
}
|
||
},
|
||
});
|
||
}
|