From bfee272ac6ffbc1789679ec04ee984e84f8bd95a Mon Sep 17 00:00:00 2001 From: kumarabhirup Date: Sat, 31 Jan 2026 22:32:09 -0800 Subject: [PATCH] agents: add engine router for multi-backend support Add router that can switch between different agent engines embedded runner vs AI SDK. Allows runtime selection based on configuration or feature flags. --- src/agents/engine-router.ts | 118 ++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/agents/engine-router.ts diff --git a/src/agents/engine-router.ts b/src/agents/engine-router.ts new file mode 100644 index 00000000000..267f9aa10a4 --- /dev/null +++ b/src/agents/engine-router.ts @@ -0,0 +1,118 @@ +/** + * LLM Engine Router for openclaw. + * + * This module provides a thin routing layer that dispatches agent runs + * to either the original pi-agent engine or the new AI SDK engine + * based on configuration. + * + * Fork-friendly: minimal integration point that reads config and routes. + */ + +import type { OpenClawConfig } from "../config/config.js"; +import type { LlmEngineType } from "../config/types.agents.js"; +import type { RunEmbeddedPiAgentParams } from "./pi-embedded-runner/run/params.js"; +import type { EmbeddedPiRunResult } from "./pi-embedded-runner/types.js"; + +/** + * Re-export for convenience. + */ +export type LlmEngine = LlmEngineType; + +/** + * Default engine to use when not configured. + */ +export const DEFAULT_ENGINE: LlmEngine = "aisdk"; + +/** + * Get the configured LLM engine from config. + * + * @param config - OpenClaw configuration + * @returns The configured engine or default + */ +export function getConfiguredEngine(config?: OpenClawConfig): LlmEngine { + const engineConfig = config?.agents?.engine; + if (engineConfig === "pi-agent" || engineConfig === "aisdk") { + return engineConfig; + } + return DEFAULT_ENGINE; +} + +/** + * Check if AI SDK engine is available (has required API keys). + */ +async function isAiSdkAvailable(): Promise { + try { + const { isAiSdkEngineAvailable } = await import("./aisdk/run.js"); + return isAiSdkEngineAvailable(); + } catch { + return false; + } +} + +/** + * Run the agent using the appropriate engine based on configuration. + * + * This is the main entry point that should be used instead of calling + * runEmbeddedPiAgent directly. It automatically routes to the correct + * engine based on config. + * + * @param params - Run parameters (same as runEmbeddedPiAgent) + * @returns Run result + */ +export async function runAgent(params: RunEmbeddedPiAgentParams): Promise { + const engine = getConfiguredEngine(params.config); + + if (engine === "aisdk") { + // Check if AI SDK is available + const available = await isAiSdkAvailable(); + if (!available) { + // Fall back to pi-agent if AI SDK is not configured + console.warn( + "[Engine Router] AI SDK engine selected but not available (missing API keys?). Falling back to pi-agent.", + ); + return runPiAgent(params); + } + + return runAiSdk(params); + } + + return runPiAgent(params); +} + +/** + * Run using pi-agent engine. + */ +async function runPiAgent(params: RunEmbeddedPiAgentParams): Promise { + const { runEmbeddedPiAgent } = await import("./pi-embedded-runner/run.js"); + return runEmbeddedPiAgent(params); +} + +/** + * Run using AI SDK engine. + */ +async function runAiSdk(params: RunEmbeddedPiAgentParams): Promise { + const { runAiSdkAgent } = await import("./aisdk/run.js"); + + // Map provider/model to AI SDK model reference + const provider = params.provider ?? "anthropic"; + const model = params.model ?? "claude-sonnet-4"; + + return runAiSdkAgent(params, { + modelRef: `${provider}/${model}`, + }); +} + +/** + * Get information about the current engine configuration. + */ +export function getEngineInfo(config?: OpenClawConfig): { + current: LlmEngine; + default: LlmEngine; + configPath: string; +} { + return { + current: getConfiguredEngine(config), + default: DEFAULT_ENGINE, + configPath: "agents.engine", + }; +}