UNPKG

langchain

Version:
90 lines (89 loc) • 3.55 kB
const require_chat_models_universal = require("../../chat_models/universal.cjs"); const require_middleware = require("../middleware.cjs"); let _langchain_core_messages = require("@langchain/core/messages"); let zod_v4 = require("zod/v4"); let _langchain_core_tracers_tracer_langchain = require("@langchain/core/tracers/tracer_langchain"); // src/agents/middleware/modelRouter.ts const QUESTION_ID = "model_route"; /** * Select an agent's model with a decision model's `Choice` answer (beta). * * The middleware classifies the latest human message once before an agent * run, stores the complete `ChoiceAnswer` in agent state as `modelRoute`, and * uses its selected label for every model call in the run. Keeping the * complete answer makes probabilities and confidence available in state and * traces. Classifier failures propagate and terminate the run rather than * silently selecting a different model. * * @example * ```ts * import { createAgent, modelRouterMiddleware } from "langchain"; * * const router = modelRouterMiddleware({ * choices: { * fast: { * model: "openai:gpt-5-mini", * criteria: "Simple, well-scoped tasks.", * }, * powerful: { * model: powerfulModel, * criteria: "Complex tasks requiring deeper reasoning.", * }, * }, * instructions: "Choose the least costly model suited to the task.", * classifier, * }); * const agent = createAgent({ * model: "openai:gpt-5-mini", * middleware: [router], * }); * ``` * * @throws Error if `choices` is empty. */ function modelRouterMiddleware(config) { const { choices, instructions, classifier } = config; const routes = Object.keys(choices); if (routes.length === 0) throw new Error("modelRouterMiddleware requires at least one choice."); const questions = { [QUESTION_ID]: { type: "choice", instructions, criteria: Object.fromEntries(routes.map((route) => [route, choices[route].criteria])) } }; const models = /* @__PURE__ */ new Map(); const modelFor = (route) => { let pending = models.get(route); if (pending === void 0) { const { model } = choices[route]; pending = Promise.resolve(typeof model === "string" ? require_chat_models_universal.initChatModel(model) : model); pending.catch(() => models.delete(route)); models.set(route, pending); } return pending; }; return require_middleware.createMiddleware({ name: "ModelRouterMiddleware", stateSchema: zod_v4.z.object({ modelRoute: zod_v4.z.custom().optional() }), tracePolicy: { processInputs: _langchain_core_tracers_tracer_langchain.omitPayload }, async beforeAgent(state, runtime) { const message = [...state.messages].reverse().find((candidate) => _langchain_core_messages.HumanMessage.isInstance(candidate)); if (message === void 0) throw new Error("modelRouterMiddleware requires a HumanMessage in the agent state to route on."); const answer = (await classifier.invoke({ state: message, questions }, { signal: runtime.signal })).choices[QUESTION_ID]; if (!Object.hasOwn(choices, answer.choice)) throw new Error(`modelRouterMiddleware: the classifier selected unknown route "${answer.choice}".`); return { modelRoute: answer }; }, async wrapModelCall(request, handler) { const route = request.state.modelRoute; if (route === void 0) throw new Error("modelRouterMiddleware found no routing answer in state."); return handler({ ...request, model: await modelFor(route.choice) }); } }); } exports.modelRouterMiddleware = modelRouterMiddleware; //# sourceMappingURL=modelRouter.cjs.map